fp@922: /****************************************************************************** fp@922: * fp@922: * $Id$ fp@922: * fp@2433: * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH fp@922: * fp@922: * This file is part of the IgH EtherCAT Master. fp@922: * fp@1326: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1326: * modify it under the terms of the GNU General Public License version 2, as fp@1326: * published by the Free Software Foundation. fp@922: * fp@1326: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1326: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1326: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1326: * Public License for more details. fp@922: * fp@1326: * You should have received a copy of the GNU General Public License along fp@1326: * with the IgH EtherCAT Master; if not, write to the Free Software fp@922: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@922: * fp@1363: * --- fp@1363: * fp@1363: * The license mentioned above concerns the source code only. Using the fp@1363: * EtherCAT technology and brand is only permitted in compliance with the fp@1363: * industrial property and similar rights of Beckhoff Automation GmbH. fp@922: * fp@922: *****************************************************************************/ fp@922: fp@922: /** fp@922: \file fp@922: EtherCAT master character device. fp@922: */ fp@922: fp@922: /*****************************************************************************/ fp@922: fp@922: #include fp@1258: #include fp@1258: #include fp@922: fp@922: #include "cdev.h" fp@922: #include "master.h" fp@950: #include "slave_config.h" fp@1264: #include "voe_handler.h" fp@1485: #include "ethernet.h" fp@922: #include "ioctl.h" fp@922: fp@2433: /** Set to 1 to enable device operations debugging. fp@2433: */ fp@2433: #define DEBUG 0 fp@2306: fp@922: /*****************************************************************************/ fp@922: fp@1268: static int eccdev_open(struct inode *, struct file *); fp@1268: static int eccdev_release(struct inode *, struct file *); fp@1268: static long eccdev_ioctl(struct file *, unsigned int, unsigned long); fp@1268: static int eccdev_mmap(struct file *, struct vm_area_struct *); fp@1258: fp@1272: /** This is the kernel version from which the .fault member of the fp@1272: * vm_operations_struct is usable. fp@1272: */ fp@1272: #define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23) fp@1272: fp@1272: #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION pw@2682: static int eccdev_vma_fault( pw@2682: #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) pw@2682: struct vm_area_struct *, pw@2682: #endif pw@2682: struct vm_fault *); fp@1272: #else fp@1258: static struct page *eccdev_vma_nopage( fp@1258: struct vm_area_struct *, unsigned long, int *); fp@1272: #endif fp@922: fp@922: /*****************************************************************************/ fp@922: fp@1268: /** File operation callbacks for the EtherCAT character device. fp@1268: */ fp@922: static struct file_operations eccdev_fops = { fp@935: .owner = THIS_MODULE, fp@935: .open = eccdev_open, fp@935: .release = eccdev_release, fp@1258: .unlocked_ioctl = eccdev_ioctl, fp@1258: .mmap = eccdev_mmap fp@1258: }; fp@1258: fp@1268: /** Callbacks for a virtual memory area retrieved with ecdevc_mmap(). fp@1268: */ fp@1258: struct vm_operations_struct eccdev_vm_ops = { fp@1272: #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION fp@1272: .fault = eccdev_vma_fault fp@1272: #else fp@1258: .nopage = eccdev_vma_nopage fp@1272: #endif fp@922: }; fp@922: fp@922: /*****************************************************************************/ fp@922: fp@1242: /** Private data structure for file handles. fp@1242: */ fp@1242: typedef struct { fp@1268: ec_cdev_t *cdev; /**< Character device. */ fp@2433: ec_ioctl_context_t ctx; /**< Context. */ fp@1242: } ec_cdev_priv_t; fp@1242: fp@1242: /*****************************************************************************/ fp@1242: fp@922: /** Constructor. fp@2421: * fp@922: * \return 0 in case of success, else < 0 fp@922: */ fp@922: int ec_cdev_init( fp@1507: ec_cdev_t *cdev, /**< EtherCAT master character device. */ fp@1507: ec_master_t *master, /**< Parent master. */ fp@1507: dev_t dev_num /**< Device number. */ fp@1507: ) fp@922: { fp@1313: int ret; fp@1313: fp@922: cdev->master = master; fp@922: fp@922: cdev_init(&cdev->cdev, &eccdev_fops); fp@922: cdev->cdev.owner = THIS_MODULE; fp@922: fp@1313: ret = cdev_add(&cdev->cdev, fp@1507: MKDEV(MAJOR(dev_num), master->index), 1); fp@1313: if (ret) { fp@1921: EC_MASTER_ERR(master, "Failed to add character device!\n"); fp@1313: } fp@1313: fp@1313: return ret; fp@922: } fp@922: fp@922: /*****************************************************************************/ fp@922: fp@922: /** Destructor. fp@922: */ fp@922: void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML device */) fp@922: { fp@922: cdev_del(&cdev->cdev); fp@922: } fp@922: fp@922: /****************************************************************************** fp@922: * File operations fp@922: *****************************************************************************/ fp@922: fp@1092: /** Called when the cdev is opened. fp@1092: */ fp@922: int eccdev_open(struct inode *inode, struct file *filp) fp@922: { fp@922: ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev); fp@1242: ec_cdev_priv_t *priv; fp@1242: fp@1242: priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL); fp@1242: if (!priv) { fp@1922: EC_MASTER_ERR(cdev->master, fp@1922: "Failed to allocate memory for private data structure.\n"); fp@1242: return -ENOMEM; fp@1242: } fp@1242: fp@1242: priv->cdev = cdev; fp@2433: priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0; fp@2433: priv->ctx.requested = 0; fp@2433: priv->ctx.process_data = NULL; fp@2433: priv->ctx.process_data_size = 0; fp@1242: fp@1242: filp->private_data = priv; fp@1397: fp@2433: #if DEBUG fp@1931: EC_MASTER_DBG(cdev->master, 0, "File opened.\n"); fp@1397: #endif fp@922: return 0; fp@922: } fp@922: fp@922: /*****************************************************************************/ fp@922: fp@1092: /** Called when the cdev is closed. fp@1092: */ fp@922: int eccdev_release(struct inode *inode, struct file *filp) fp@922: { fp@1242: ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; fp@1242: ec_master_t *master = priv->cdev->master; fp@1242: fp@2433: if (priv->ctx.requested) { fp@1242: ecrt_release_master(master); fp@2433: } fp@2433: fp@2433: if (priv->ctx.process_data) { fp@2433: vfree(priv->ctx.process_data); fp@2433: } fp@2433: fp@2433: #if DEBUG fp@1931: EC_MASTER_DBG(master, 0, "File closed.\n"); fp@1397: #endif fp@1397: fp@1242: kfree(priv); fp@922: return 0; fp@922: } fp@922: fp@922: /*****************************************************************************/ fp@922: fp@1092: /** Called when an ioctl() command is issued. fp@1092: */ fp@935: long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) fp@935: { fp@1242: ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; fp@2433: fp@2433: #if DEBUG fp@2433: EC_MASTER_DBG(priv->cdev->master, 0, fp@2433: "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n", fp@2433: filp, cmd, _IOC_NR(cmd), arg); fp@2433: #endif fp@2433: fp@2433: return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg); fp@1079: } fp@1079: fp@1079: /*****************************************************************************/ fp@1258: fp@2516: #ifndef VM_DONTDUMP fp@2516: /** VM_RESERVED disappeared in 3.7. fp@2516: */ fp@2516: #define VM_DONTDUMP VM_RESERVED fp@2516: #endif fp@2516: fp@1268: /** Memory-map callback for the EtherCAT character device. fp@1268: * fp@1268: * The actual mapping will be done in the eccdev_vma_nopage() callback of the fp@1268: * virtual memory area. fp@2522: * fp@2522: * \return Always zero (success). fp@1268: */ fp@1258: int eccdev_mmap( fp@1258: struct file *filp, fp@1258: struct vm_area_struct *vma fp@1258: ) fp@1258: { fp@1258: ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data; fp@1258: fp@1921: EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n"); fp@1258: fp@1258: vma->vm_ops = &eccdev_vm_ops; fp@2516: vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */ fp@1258: vma->vm_private_data = priv; fp@1258: fp@1258: return 0; fp@1258: } fp@1258: fp@1258: /*****************************************************************************/ fp@1258: fp@1272: #if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION fp@1272: fp@1268: /** Page fault callback for a virtual memory area. fp@1268: * fp@1268: * Called at the first access on a virtual-memory area retrieved with fp@1268: * ecdev_mmap(). fp@2522: * fp@2522: * \return Zero on success, otherwise a negative error code. fp@1268: */ fp@1272: static int eccdev_vma_fault( fp@2681: #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0) fp@1272: struct vm_area_struct *vma, /**< Virtual memory area. */ fp@2681: #endif fp@1272: struct vm_fault *vmf /**< Fault data. */ fp@1272: ) fp@1272: { fp@2681: #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) fp@2681: struct vm_area_struct *vma = vmf->vma; fp@2681: #endif fp@1284: unsigned long offset = vmf->pgoff << PAGE_SHIFT; fp@1284: ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data; fp@1272: struct page *page; fp@1284: fp@2522: if (offset >= priv->ctx.process_data_size) { fp@1272: return VM_FAULT_SIGBUS; fp@2522: } fp@1272: fp@2433: page = vmalloc_to_page(priv->ctx.process_data + offset); fp@2522: if (!page) { fp@1284: return VM_FAULT_SIGBUS; fp@2522: } fp@1272: fp@1272: get_page(page); fp@1272: vmf->page = page; fp@1284: pw@2682: EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault," pw@2682: " offset = %lu, page = %p\n", offset, page); fp@1284: fp@1272: return 0; fp@1272: } fp@1272: fp@1272: #else fp@1272: fp@1272: /** Nopage callback for a virtual memory area. fp@1272: * fp@1272: * Called at the first access on a virtual-memory area retrieved with fp@1272: * ecdev_mmap(). fp@1272: */ fp@1258: struct page *eccdev_vma_nopage( fp@1268: struct vm_area_struct *vma, /**< Virtual memory area initialized by fp@1268: the kernel. */ fp@1268: unsigned long address, /**< Requested virtual address. */ fp@1268: int *type /**< Type output parameter. */ fp@1258: ) fp@1258: { fp@1258: unsigned long offset; fp@1258: struct page *page = NOPAGE_SIGBUS; fp@1258: ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data; fp@2061: ec_master_t *master = priv->cdev->master; fp@1258: fp@1258: offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT); fp@1258: fp@2433: if (offset >= priv->ctx.process_data_size) fp@1258: return NOPAGE_SIGBUS; fp@1258: fp@2433: page = vmalloc_to_page(priv->ctx.process_data + offset); fp@1258: fp@2061: EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx," fp@1921: " offset = %#lx, page = %p\n", address, offset, page); fp@1258: fp@1258: get_page(page); fp@1258: if (type) fp@1258: *type = VM_FAULT_MINOR; fp@1258: fp@1258: return page; fp@1258: } fp@1258: fp@1272: #endif fp@1272: fp@1272: /*****************************************************************************/