From: Graeme Foot <Graeme.Foot@touchcut.com>
Date: Wed Apr 05 14:39:54 2017 +1200

Allow the application to explicitly process slave requests cyclically from the
applications realtime context

diff --git a/include/ecrt.h b/include/ecrt.h
--- a/include/ecrt.h
+++ b/include/ecrt.h
@@ -1149,6 +1149,34 @@
         ec_master_t *master /**< EtherCAT master. */
         );
 
+/** Selects whether to process slave requests by the application or the master
+ *
+ * if rt_slave_requests \a True, slave requests are to be handled by calls to 
+ * ecrt_master_exec_requests() from the applications realtime context,
+ * otherwise the master will handle them from its operation thread
+ *
+ * \return 0 on success, otherwise negative error code.
+ */
+int ecrt_master_rt_slave_requests(
+        ec_master_t *master, /**< EtherCAT master. */
+        unsigned int rt_slave_requests /**< if \a True, slave requests are
+                                       to behandled by calls to 
+                                      ecrt_master_exec_requests() from
+                                      the applications realtime context. */
+        );
+
+/** Explicit call to process slave requests.
+ *
+ * This needs to be called on a cyclical period by the applications
+ * realtime context if ecrt_master_rt_slave_requests() has been called
+ * with rt_slave_requests set to true.  If rt_slave_requests is \a False
+ * (the default) slave requests will be processed within the master and
+ * this call will be ignored.
+ */
+void ecrt_master_exec_slave_requests(
+        ec_master_t *master /**< EtherCAT master. */
+        );
+
 /** Retry configuring slaves.
  *
  * Via this method, the application can tell the master to bring all slaves to
diff --git a/lib/master.c b/lib/master.c
--- a/lib/master.c
+++ b/lib/master.c
@@ -857,6 +857,37 @@
 
 /****************************************************************************/
 
+int ecrt_master_rt_slave_requests(ec_master_t *master,
+        unsigned int rt_slave_requests)
+{
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_RT_SLAVE_REQUESTS, rt_slave_requests);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        EC_PRINT_ERR("Failed to set rt slave request (%s): %s\n",
+                (rt_slave_requests) ? "True" : "False",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
+    }
+
+    return 0;
+}
+
+/****************************************************************************/
+
+void ecrt_master_exec_slave_requests(ec_master_t *master)
+{
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_EXEC_SLAVE_REQUESTS, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        EC_PRINT_ERR("Failed to process slave requests: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+}
+
+/****************************************************************************/
+
 void ecrt_master_reset(ec_master_t *master)
 {
     int ret;
diff --git a/master/fsm_master.c b/master/fsm_master.c
--- a/master/fsm_master.c
+++ b/master/fsm_master.c
@@ -235,6 +235,7 @@
                 "link down on %s device. Clearing slave list.\n",
                 ec_device_names[fsm->dev_idx != 0]);
 
+        ec_master_slaves_not_available(master);
 #ifdef EC_EOE
         ec_master_eoe_stop(master);
         ec_master_clear_eoe_handlers(master);
@@ -289,6 +290,7 @@
             fsm->idle = 0;
             fsm->scan_jiffies = jiffies;
 
+            ec_master_slaves_not_available(master);
 #ifdef EC_EOE
             ec_master_eoe_stop(master);
             ec_master_clear_eoe_handlers(master);
@@ -350,6 +352,9 @@
             }
 
             ec_fsm_master_enter_clear_addresses(fsm);
+
+            ec_master_slaves_available(master);
+            
             return;
         }
     }
diff --git a/master/ioctl.c b/master/ioctl.c
--- a/master/ioctl.c
+++ b/master/ioctl.c
@@ -2211,6 +2211,61 @@
 
 /*****************************************************************************/
 
+/** Call to set whether processing slave requests explicitly from the
+ * application is active or not.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_rt_slave_requests(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    unsigned long rt_slave_requests = (unsigned long) arg;
+    int ret = 0;
+
+    if (unlikely(!ctx->requested)) {
+        ret = -EPERM;
+        goto out_return;
+    }
+
+    if (down_interruptible(&master->master_sem)) {
+        ret = -EINTR;
+        goto out_return;
+    }
+
+    ecrt_master_rt_slave_requests(master, rt_slave_requests);
+
+    up(&master->master_sem);
+    
+out_return:
+    return ret;
+}
+
+/*****************************************************************************/
+
+/** Call to process slave requests explicitly from application.
+ *
+ * \return Always zero (success).
+ */
+static ATTRIBUTES int ec_ioctl_exec_slave_requests(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    ecrt_master_exec_slave_requests(master);
+    
+    return 0;
+}
+
+/*****************************************************************************/
+
 /** Reset configuration.
  *
  * \return Always zero (success).
@@ -4520,6 +4575,20 @@
             }
             ret = ec_ioctl_sync_mon_process(master, arg, ctx);
             break;
+        case EC_IOCTL_RT_SLAVE_REQUESTS:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_rt_slave_requests(master, arg, ctx);
+            break;
+        case EC_IOCTL_EXEC_SLAVE_REQUESTS:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_exec_slave_requests(master, arg, ctx);
+            break;
         case EC_IOCTL_RESET:
             if (!ctx->writable) {
                 ret = -EPERM;
diff --git a/master/ioctl.h b/master/ioctl.h
--- a/master/ioctl.h
+++ b/master/ioctl.h
@@ -113,6 +113,8 @@
 #define EC_IOCTL_64_REF_CLK_TIME       EC_IOR(0x63, uint64_t)
 #define EC_IOCTL_SYNC_MON_QUEUE         EC_IO(0x2c)
 #define EC_IOCTL_SYNC_MON_PROCESS      EC_IOR(0x2d, uint32_t)
+#define EC_IOCTL_RT_SLAVE_REQUESTS     EC_IOW(0x64, uint32_t)
+#define EC_IOCTL_EXEC_SLAVE_REQUESTS    EC_IO(0x65)
 #define EC_IOCTL_RESET                  EC_IO(0x2e)
 #define EC_IOCTL_SC_SYNC               EC_IOW(0x2f, ec_ioctl_config_t)
 #define EC_IOCTL_SC_WATCHDOG           EC_IOW(0x30, ec_ioctl_config_t)
diff --git a/master/master.c b/master/master.c
--- a/master/master.c
+++ b/master/master.c
@@ -196,6 +196,8 @@
 
     master->ext_ring_idx_rt = 0;
     master->ext_ring_idx_fsm = 0;
+    master->rt_slave_requests = 0;
+    master->rt_slaves_available = 0;
 
     // init external datagram ring
     for (i = 0; i < EC_EXT_RING_SIZE; i++) {
@@ -409,6 +411,7 @@
 
     ec_cdev_clear(&master->cdev);
 
+    ec_master_slaves_not_available(master);
 #ifdef EC_EOE
     ec_master_clear_eoe_handlers(master);
 #endif
@@ -472,6 +475,26 @@
 
 /*****************************************************************************/
 
+/** Set flag to say that the slaves are not available for slave request
+ * processing.
+ */
+void ec_master_slaves_not_available(ec_master_t *master)
+{
+    master->rt_slaves_available = 0;
+}
+
+/*****************************************************************************/
+
+/** Set flag to say that the slaves are now available for slave request
+ * processing.
+ */
+void ec_master_slaves_available(ec_master_t *master)
+{
+    master->rt_slaves_available = 1;
+}
+
+/*****************************************************************************/
+
 /** Clear all slaves.
  */
 void ec_master_clear_slaves(ec_master_t *master)
@@ -670,6 +693,7 @@
 
     master->phase = EC_ORPHANED;
 
+    ec_master_slaves_not_available(master);
 #ifdef EC_EOE
     ec_master_eoe_stop(master);
 #endif
@@ -1443,6 +1467,12 @@
     ec_fsm_slave_t *fsm, *next;
     unsigned int count = 0;
 
+    // return if the slaves are not configured
+    if (master->rt_slave_requests && 
+            !master->rt_slaves_available) {
+        return;
+    }
+    
     list_for_each_entry_safe(fsm, next, &master->fsm_exec_list, list) {
         if (!fsm->datagram) {
             EC_MASTER_WARN(master, "Slave %u FSM has zero datagram."
@@ -1555,6 +1585,7 @@
 
         fsm_exec = ec_fsm_master_exec(&master->fsm);
 
+        // idle thread will still be in charge of calling the slave requests
         ec_master_exec_slave_fsms(master);
 
         up(&master->master_sem);
@@ -1622,7 +1653,11 @@
                 master->injection_seq_fsm++;
             }
 
-            ec_master_exec_slave_fsms(master);
+            // if rt_slave_requests is true this will be handled by the
+            // app explicitly by calling ecrt_master_exec_slave_request()
+            if (!master->rt_slave_requests) {
+                ec_master_exec_slave_fsms(master);
+            }
 
             up(&master->master_sem);
         }
@@ -3333,6 +3368,38 @@
 
 /*****************************************************************************/
 
+int ecrt_master_rt_slave_requests(ec_master_t *master, 
+        unsigned int rt_slave_requests)
+{
+    // set flag as to whether the master or the external application
+    // should be handling processing the slave request
+    master->rt_slave_requests = rt_slave_requests;
+    
+    if (master->rt_slave_requests) {
+        EC_MASTER_INFO(master, "Application selected to process"
+                " slave request by the application.\n");
+    }
+    else {
+        EC_MASTER_INFO(master, "Application selected to process"
+                " slave request by the master.\n");
+    }
+    
+    return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_exec_slave_requests(ec_master_t *master)
+{
+    // ignore this call if the master is not operational or not set to
+    // handle the slave requests from the application
+    if (master->rt_slave_requests && (master->phase == EC_OPERATION)) {
+        ec_master_exec_slave_fsms(master);
+    }
+}
+
+/*****************************************************************************/
+
 void ecrt_master_reset(ec_master_t *master)
 {
     ec_slave_config_t *sc;
@@ -3376,6 +3443,8 @@
 EXPORT_SYMBOL(ecrt_master_sdo_upload);
 EXPORT_SYMBOL(ecrt_master_write_idn);
 EXPORT_SYMBOL(ecrt_master_read_idn);
+EXPORT_SYMBOL(ecrt_master_rt_slave_requests);
+EXPORT_SYMBOL(ecrt_master_exec_slave_requests);
 EXPORT_SYMBOL(ecrt_master_reset);
 
 /** \endcond */
diff --git a/master/master.h b/master/master.h
--- a/master/master.h
+++ b/master/master.h
@@ -282,6 +282,16 @@
     unsigned int send_interval; /**< Interval between two calls to
                                   ecrt_master_send(). */
     size_t max_queue_size; /**< Maximum size of datagram queue */
+    unsigned int rt_slave_requests; /**< if \a True, slave requests are to be
+                                      handled by calls to 
+                                      ecrt_master_exec_requests() from
+                                      the applications realtime context. */
+    unsigned int rt_slaves_available; /**< if \a True, slave requests
+                                        can be handled by calls to 
+                                        ecrt_master_exec_requests() from
+                                        the applications realtime context.
+                                        Otherwise the master is currently
+                                        configuring the slaves */
 
     ec_slave_t *fsm_slave; /**< Slave that is queried next for FSM exec. */
     struct list_head fsm_exec_list; /**< Slave FSM execution list. */
@@ -359,6 +369,8 @@
 #ifdef EC_EOE
 void ec_master_clear_eoe_handlers(ec_master_t *);
 #endif
+void ec_master_slaves_not_available(ec_master_t *);
+void ec_master_slaves_available(ec_master_t *);
 void ec_master_clear_slaves(ec_master_t *);
 
 unsigned int ec_master_config_count(const ec_master_t *);
