From 3e421e3cddfa582888a2a4b287ca7d08ad47ec4e Mon Sep 17 00:00:00 2001 From: Alex Forencich Date: Thu, 1 Jan 2026 15:07:50 -0800 Subject: [PATCH] cndm: Implement mmap and ioctl Signed-off-by: Alex Forencich --- src/cndm/modules/cndm/cndm.h | 5 +- src/cndm/modules/cndm/cndm_dev.c | 101 +++++++++++++++++++++++++++++ src/cndm/modules/cndm/cndm_ioctl.h | 63 ++++++++++++++++++ src/cndm/modules/cndm/cndm_main.c | 29 +++++---- 4 files changed, 182 insertions(+), 16 deletions(-) create mode 100644 src/cndm/modules/cndm/cndm_ioctl.h diff --git a/src/cndm/modules/cndm/cndm.h b/src/cndm/modules/cndm/cndm.h index 0dbacd2..672bff2 100644 --- a/src/cndm/modules/cndm/cndm.h +++ b/src/cndm/modules/cndm/cndm.h @@ -45,8 +45,9 @@ struct cndm_dev { struct net_device *ndev[32]; - void __iomem *bar; - resource_size_t bar_len; + resource_size_t hw_regs_size; + phys_addr_t hw_regs_phys; + void __iomem *hw_addr; u32 port_count; u32 port_offset; diff --git a/src/cndm/modules/cndm/cndm_dev.c b/src/cndm/modules/cndm/cndm_dev.c index 7f3c2b3..e4b011e 100644 --- a/src/cndm/modules/cndm/cndm_dev.c +++ b/src/cndm/modules/cndm/cndm_dev.c @@ -9,6 +9,7 @@ Authors: */ #include "cndm.h" +#include "cndm_ioctl.h" #include @@ -28,8 +29,108 @@ static int cndm_release(struct inode *inode, struct file *file) return 0; } +static int cndm_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct miscdevice *miscdev = file->private_data; + struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev); + int index; + u64 pgoff, req_len, req_start; + + index = vma->vm_pgoff >> (40 - PAGE_SHIFT); + req_len = vma->vm_end - vma->vm_start; + pgoff = vma->vm_pgoff & ((1U << (40 - PAGE_SHIFT)) - 1); + req_start = pgoff << PAGE_SHIFT; + + if (vma->vm_end < vma->vm_start) + return -EINVAL; + + if ((vma->vm_flags & VM_SHARED) == 0) + return -EINVAL; + + switch (index) { + case 0: + if (req_start + req_len > cdev->hw_regs_size) + return -EINVAL; + + return io_remap_pfn_range(vma, vma->vm_start, + (cdev->hw_regs_phys >> PAGE_SHIFT) + pgoff, + req_len, pgprot_noncached(vma->vm_page_prot)); + default: + return -EINVAL; + } + + return -EINVAL; +} + +static long cndm_ioctl(struct file *file, unsigned int cmd, unsigned long arg) +{ + struct miscdevice *miscdev = file->private_data; + struct cndm_dev *cdev = container_of(miscdev, struct cndm_dev, misc_dev); + size_t minsz; + + if (cmd == CNDM_IOCTL_GET_API_VERSION) { + // Get API version + return CNDM_IOCTL_API_VERSION; + } else if (cmd == CNDM_IOCTL_GET_DEVICE_INFO) { + // Get device information + struct cndm_ioctl_device_info info; + + minsz = offsetofend(struct cndm_ioctl_device_info, num_irqs); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + info.num_regions = 1; + info.num_irqs = 0; + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + } else if (cmd == CNDM_IOCTL_GET_REGION_INFO) { + // Get region information + struct cndm_ioctl_region_info info; + + minsz = offsetofend(struct cndm_ioctl_region_info, name); + + if (copy_from_user(&info, (void __user *)arg, minsz)) + return -EFAULT; + + if (info.argsz < minsz) + return -EINVAL; + + info.flags = 0; + info.type = CNDM_REGION_TYPE_UNIMPLEMENTED; + info.next = 0; + info.child = 0; + info.size = 0; + info.offset = ((u64)info.index) << 40; + info.name[0] = 0; + + switch (info.index) { + case 0: + info.type = CNDM_REGION_TYPE_NIC_CTRL; + info.next = 0; + info.child = 0; + info.size = cdev->hw_regs_size; + info.offset = ((u64)info.index) << 40; + strscpy(info.name, "ctrl", sizeof(info.name)); + break; + default: + return -EINVAL; + } + + return copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0; + } + + return -EINVAL; +} + const struct file_operations cndm_fops = { .owner = THIS_MODULE, .open = cndm_open, .release = cndm_release, + .mmap = cndm_mmap, + .unlocked_ioctl = cndm_ioctl, }; diff --git a/src/cndm/modules/cndm/cndm_ioctl.h b/src/cndm/modules/cndm/cndm_ioctl.h new file mode 100644 index 0000000..15cceaa --- /dev/null +++ b/src/cndm/modules/cndm/cndm_ioctl.h @@ -0,0 +1,63 @@ +/* SPDX-License-Identifier: GPL */ +/* + +Copyright (c) 2025 FPGA Ninja, LLC + +Authors: +- Alex Forencich + +*/ + +#ifndef CNDM_IOCTL_H +#define CNDM_IOCTL_H + +#include + +#define CNDM_IOCTL_API_VERSION 0 + +#define CNDM_IOCTL_TYPE 0x63 +#define CNDM_IOCTL_BASE 0xC0 + +enum { + CNDM_REGION_TYPE_UNIMPLEMENTED = 0x00000000, + CNDM_REGION_TYPE_CTRL = 0x00001000, + CNDM_REGION_TYPE_NIC_CTRL = 0x00001001, + CNDM_REGION_TYPE_APP_CTRL = 0x00001002, +}; + +// get API version +#define CNDM_IOCTL_GET_API_VERSION _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 0) + +// get device information +struct cndm_ioctl_device_info { + __u32 argsz; + __u32 flags; + __u32 fw_id; + __u32 fw_ver; + __u32 board_id; + __u32 board_ver; + __u32 build_date; + __u32 git_hash; + __u32 rel_info; + __u32 num_regions; + __u32 num_irqs; +}; + +#define CNDM_IOCTL_GET_DEVICE_INFO _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 1) + +// get region information +struct cndm_ioctl_region_info { + __u32 argsz; + __u32 flags; + __u32 index; + __u32 type; + __u32 next; + __u32 child; + __u32 size; + __u64 offset; + __u8 name[32]; +}; + +#define CNDM_IOCTL_GET_REGION_INFO _IO(CNDM_IOCTL_TYPE, CNDM_IOCTL_BASE + 2) + +#endif diff --git a/src/cndm/modules/cndm/cndm_main.c b/src/cndm/modules/cndm/cndm_main.c index 515ebbe..dc7211a 100644 --- a/src/cndm/modules/cndm/cndm_main.c +++ b/src/cndm/modules/cndm/cndm_main.c @@ -52,9 +52,9 @@ static int cndm_common_probe(struct cndm_dev *cdev) devlink_register(devlink, dev); #endif - cdev->port_count = ioread32(cdev->bar + 0x0100); - cdev->port_offset = ioread32(cdev->bar + 0x0104); - cdev->port_stride = ioread32(cdev->bar + 0x0108); + cdev->port_count = ioread32(cdev->hw_addr + 0x0100); + cdev->port_offset = ioread32(cdev->hw_addr + 0x0104); + cdev->port_stride = ioread32(cdev->hw_addr + 0x0108); dev_info(dev, "Port count: %d", cdev->port_count); dev_info(dev, "Port offset: 0x%x", cdev->port_offset); @@ -63,7 +63,7 @@ static int cndm_common_probe(struct cndm_dev *cdev) for (k = 0; k < cdev->port_count; k++) { struct net_device *ndev; - ndev = cndm_create_netdev(cdev, k, cdev->bar + cdev->port_offset + (cdev->port_stride*k)); + ndev = cndm_create_netdev(cdev, k, cdev->hw_addr + cdev->port_offset + (cdev->port_stride*k)); if (IS_ERR_OR_NULL(ndev)) { ret = PTR_ERR(ndev); goto fail_netdev; @@ -155,17 +155,18 @@ static int cndm_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto fail_regions; } - cdev->bar_len = pci_resource_len(pdev, 0); + cdev->hw_regs_size = pci_resource_len(pdev, 0); + cdev->hw_regs_phys = pci_resource_start(pdev, 0); - dev_info(dev, "BAR size: %llu", cdev->bar_len); - cdev->bar = pci_ioremap_bar(pdev, 0); - if (!cdev->bar) { + dev_info(dev, "Control BAR size: %llu", cdev->hw_regs_size); + cdev->hw_addr = pci_ioremap_bar(pdev, 0); + if (!cdev->hw_addr) { ret = -ENOMEM; - dev_err(dev, "Failed to map BAR 0"); + dev_err(dev, "Failed to map control BAR"); goto fail_map_bars; } - if (ioread32(cdev->bar + 0x0000) == 0xffffffff) { + if (ioread32(cdev->hw_addr + 0x0000) == 0xffffffff) { ret = -EIO; dev_err(dev, "Device needs to be reset"); goto fail_map_bars; @@ -187,8 +188,8 @@ fail_common: cndm_irq_deinit_pcie(cdev); fail_init_irq: fail_map_bars: - if (cdev->bar) - pci_iounmap(pdev, cdev->bar); + if (cdev->hw_addr) + pci_iounmap(pdev, cdev->hw_addr); pci_release_regions(pdev); fail_regions: pci_clear_master(pdev); @@ -211,8 +212,8 @@ static void cndm_pci_remove(struct pci_dev *pdev) cndm_common_remove(cdev); cndm_irq_deinit_pcie(cdev); - if (cdev->bar) - pci_iounmap(pdev, cdev->bar); + if (cdev->hw_addr) + pci_iounmap(pdev, cdev->hw_addr); pci_release_regions(pdev); pci_clear_master(pdev); pci_disable_device(pdev);