master/cdev.c
changeset 1258 900f1124e8f8
parent 1255 38b7e05b20c1
child 1259 5f9d1abbee71
--- a/master/cdev.c	Fri Oct 10 07:58:48 2008 +0000
+++ b/master/cdev.c	Fri Oct 10 08:34:15 2008 +0000
@@ -39,6 +39,8 @@
 /*****************************************************************************/
 
 #include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
 
 #include "cdev.h"
 #include "master.h"
@@ -52,6 +54,10 @@
 int eccdev_open(struct inode *, struct file *);
 int eccdev_release(struct inode *, struct file *);
 long eccdev_ioctl(struct file *, unsigned int, unsigned long);
+int eccdev_mmap(struct file *, struct vm_area_struct *);
+
+static struct page *eccdev_vma_nopage(
+        struct vm_area_struct *, unsigned long, int *);
 
 /*****************************************************************************/
 
@@ -59,7 +65,12 @@
     .owner          = THIS_MODULE,
     .open           = eccdev_open,
     .release        = eccdev_release,
-    .unlocked_ioctl = eccdev_ioctl
+    .unlocked_ioctl = eccdev_ioctl,
+    .mmap           = eccdev_mmap
+};
+
+struct vm_operations_struct eccdev_vm_ops = {
+    .nopage = eccdev_vma_nopage
 };
 
 /** \endcond */
@@ -71,6 +82,8 @@
 typedef struct {
     ec_cdev_t *cdev;
     unsigned int requested;
+    uint8_t *process_data;
+    size_t process_data_size;
 } ec_cdev_priv_t;
 
 /*****************************************************************************/
@@ -1462,12 +1475,48 @@
         ec_cdev_priv_t *priv /**< Private data structure of file handle. */
         )
 {
+    ec_domain_t *domain;
+    off_t offset;
+    
 	if (unlikely(!priv->requested))
 		return -EPERM;
 
+    /* Get the sum of the domains' process data sizes. */
+    
+    priv->process_data_size = 0;
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    list_for_each_entry(domain, &master->domains, list) {
+        priv->process_data_size += ecrt_domain_size(domain);
+    }
+    
+    up(&master->master_sem);
+
+    if (priv->process_data_size) {
+        priv->process_data = vmalloc(priv->process_data_size);
+        if (!priv->process_data) {
+            priv->process_data_size = 0;
+            return -ENOMEM;
+        }
+
+        /* Set the memory as external process data memory for the domains. */
+
+        offset = 0;
+        list_for_each_entry(domain, &master->domains, list) {
+            ecrt_domain_external_memory(domain, priv->process_data + offset);
+            offset += ecrt_domain_size(domain);
+        }
+    }
+
     if (ecrt_master_activate(master))
         return -EIO;
 
+    if (copy_to_user((void __user *) arg,
+                &priv->process_data_size, sizeof(size_t)))
+        return -EFAULT;
+
     return 0;
 }
 
@@ -1788,6 +1837,94 @@
     return ret;
 }
 
+/*****************************************************************************/
+
+/** Gets the domain's offset in the total process data.
+ */
+int ec_cdev_ioctl_domain_offset(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned long arg, /**< ioctl() argument. */
+        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
+        )
+{
+    int offset = 0;
+    const ec_domain_t *domain;
+
+	if (unlikely(!priv->requested))
+        return -EPERM;
+
+    if (down_interruptible(&master->master_sem)) {
+        return -EINTR;
+    }
+
+    list_for_each_entry(domain, &master->domains, list) {
+        if (domain->index == arg) {
+            up(&master->master_sem);
+            return offset;
+        }
+        offset += ecrt_domain_size(domain);
+    }
+
+    up(&master->master_sem);
+    return -ESRCH;
+}
+
+/*****************************************************************************/
+
+/** Process the domain.
+ */
+int ec_cdev_ioctl_domain_process(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned long arg, /**< ioctl() argument. */
+        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
+        )
+{
+    ec_domain_t *domain;
+
+	if (unlikely(!priv->requested))
+        return -EPERM;
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(domain = ec_master_find_domain(master, arg))) {
+        up(&master->master_sem);
+        return -ESRCH;
+    }
+
+    ecrt_domain_process(domain);
+    up(&master->master_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the domain.
+ */
+int ec_cdev_ioctl_domain_queue(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned long arg, /**< ioctl() argument. */
+        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
+        )
+{
+    ec_domain_t *domain;
+
+	if (unlikely(!priv->requested))
+        return -EPERM;
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(domain = ec_master_find_domain(master, arg))) {
+        up(&master->master_sem);
+        return -ESRCH;
+    }
+
+    ecrt_domain_queue(domain);
+    up(&master->master_sem);
+    return 0;
+}
+
 /******************************************************************************
  * File operations
  *****************************************************************************/
@@ -1808,6 +1945,8 @@
 
     priv->cdev = cdev;
     priv->requested = 0;
+    priv->process_data = NULL;
+    priv->process_data_size = 0;
 
     filp->private_data = priv;
     if (master->debug_level)
@@ -1827,6 +1966,9 @@
     if (priv->requested)
         ecrt_release_master(master);
 
+    if (priv->process_data)
+        vfree(priv->process_data);
+
     if (master->debug_level)
         EC_DBG("File closed.\n");
     kfree(priv);
@@ -1953,9 +2095,68 @@
             if (!(filp->f_mode & FMODE_WRITE))
 				return -EPERM;
 			return ec_cdev_ioctl_sc_sdo(master, arg, priv);
+        case EC_IOCTL_DOMAIN_OFFSET:
+			return ec_cdev_ioctl_domain_offset(master, arg, priv);
+        case EC_IOCTL_DOMAIN_PROCESS:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_domain_process(master, arg, priv);
+        case EC_IOCTL_DOMAIN_QUEUE:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_domain_queue(master, arg, priv);
         default:
             return -ENOTTY;
     }
 }
 
 /*****************************************************************************/
+
+int eccdev_mmap(
+        struct file *filp,
+        struct vm_area_struct *vma
+        )
+{
+    ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
+
+    if (priv->cdev->master->debug_level)
+        EC_DBG("mmap()\n");
+
+    vma->vm_ops = &eccdev_vm_ops;
+    vma->vm_flags |= VM_RESERVED; /* Pages will not be swapped out */
+    vma->vm_private_data = priv;
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+struct page *eccdev_vma_nopage(
+        struct vm_area_struct *vma,
+        unsigned long address,
+        int *type
+        )
+{
+    unsigned long offset;
+    struct page *page = NOPAGE_SIGBUS;
+    ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
+
+    offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+
+    if (offset >= priv->process_data_size)
+        return NOPAGE_SIGBUS;
+
+    page = vmalloc_to_page(priv->process_data + offset);
+
+    if (priv->cdev->master->debug_level)
+        EC_DBG("Nopage fault vma, address = %#lx, offset = %#lx, page = %p\n",
+                address, offset, page);
+
+    get_page(page);
+    if (type)
+        *type = VM_FAULT_MINOR;
+
+    return page;
+}
+
+/*****************************************************************************/