master/cdev.c
changeset 1200 ce1a65f06efc
parent 1196 a27966f01b72
child 1209 8be462afb7f4
--- a/master/cdev.c	Wed Aug 13 13:21:35 2008 +0000
+++ b/master/cdev.c	Wed Aug 13 13:23:52 2008 +0000
@@ -986,6 +986,162 @@
 
 /*****************************************************************************/
 
+/** Read a slave's physical memory.
+ */
+int ec_cdev_ioctl_slave_phy_read(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned long arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_phy_t data;
+    ec_slave_t *slave;
+    uint8_t *contents;
+    ec_phy_request_t request;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    if (!data.length)
+        return 0;
+
+    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
+        EC_ERR("Failed to allocate %u bytes for phy data.\n", data.length);
+        return -ENOMEM;
+    }
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(slave = ec_master_find_slave(
+                    master, 0, data.slave_position))) {
+        up(&master->master_sem);
+        EC_ERR("Slave %u does not exist!\n", data.slave_position);
+        return -EINVAL;
+    }
+
+    // init phy request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.dir = EC_DIR_INPUT;
+    request.data = contents;
+    request.offset = data.offset;
+    request.length = data.length;
+    request.state = EC_REQUEST_QUEUED;
+
+    // schedule request.
+    list_add_tail(&request.list, &master->phy_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(master->phy_queue,
+                request.state != EC_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.state == EC_REQUEST_QUEUED) {
+            // abort request
+            list_del(&request.list);
+            up(&master->master_sem);
+            kfree(contents);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(master->phy_queue, request.state != EC_REQUEST_BUSY);
+
+    if (request.state == EC_REQUEST_SUCCESS) {
+        if (copy_to_user((void __user *) data.data, contents, data.length))
+            return -EFAULT;
+    }
+    kfree(contents);
+
+    return request.state == EC_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's physical memory.
+ */
+int ec_cdev_ioctl_slave_phy_write(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned long arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_phy_t data;
+    ec_slave_t *slave;
+    uint8_t *contents;
+    ec_phy_request_t request;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    if (!data.length)
+        return 0;
+
+    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
+        EC_ERR("Failed to allocate %u bytes for phy data.\n", data.length);
+        return -ENOMEM;
+    }
+
+    if (copy_from_user(contents, (void __user *) data.data, data.length)) {
+        kfree(contents);
+        return -EFAULT;
+    }
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    if (!(slave = ec_master_find_slave(
+                    master, 0, data.slave_position))) {
+        up(&master->master_sem);
+        EC_ERR("Slave %u does not exist!\n", data.slave_position);
+        kfree(contents);
+        return -EINVAL;
+    }
+
+    // init phy request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.dir = EC_DIR_OUTPUT;
+    request.data = contents;
+    request.offset = data.offset;
+    request.length = data.length;
+    request.state = EC_REQUEST_QUEUED;
+
+    // schedule request.
+    list_add_tail(&request.list, &master->phy_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(master->phy_queue,
+                request.state != EC_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.state == EC_REQUEST_QUEUED) {
+            // abort request
+            list_del(&request.list);
+            up(&master->master_sem);
+            kfree(contents);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(master->phy_queue, request.state != EC_REQUEST_BUSY);
+
+    kfree(contents);
+
+    return request.state == EC_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
 /** Get slave configuration information.
  */
 int ec_cdev_ioctl_config(
@@ -1282,6 +1438,12 @@
             if (!(filp->f_mode & FMODE_WRITE))
                 return -EPERM;
             return ec_cdev_ioctl_slave_sii_write(master, arg);
+        case EC_IOCTL_SLAVE_PHY_READ:
+            return ec_cdev_ioctl_slave_phy_read(master, arg);
+        case EC_IOCTL_SLAVE_PHY_WRITE:
+            if (!(filp->f_mode & FMODE_WRITE))
+                return -EPERM;
+            return ec_cdev_ioctl_slave_phy_write(master, arg);
         case EC_IOCTL_CONFIG:
             return ec_cdev_ioctl_config(master, arg);
         case EC_IOCTL_CONFIG_PDO: