From: Graeme Foot <graeme.foot@touchcut.com>
Date: Mon, 01 Feb 2018 11:00:00 +1200

EoE processing requires ecrt_master_callbacks() so that an EoE processing
thread can ask your application to call ecrt_master_receive() and
ecrt_master_send_ext() if it is appropriate timing wise.  However for
RTDM user space applications this function is not available is you are
unable to supply user space callbacks to a kernel space process.

This patch adds ecrt_master_eoe_is_open(), ecrt_master_eoe_process() and
makes ecrt_master_send_ext() available to user space applications.  Once
the master goes active, and no callbacks have been specified your application
can take over processing the external datagrams with something like this:

# SEM *master_sem;
# SEM *rt_thread_sem;
#
# int wait_on_rt_thread()
# {
#   if (((int64_t)(next_wake_time - curr_time)) < CRITICAL_CYCLE_TIME)
#   {
#     rt_sem_wait_timed(rt_thread_sem, 1000000000LL);
#   }
#   else
#   {
#     return 1;
#   }
# }
# 
# int rt_thread_complete()
# {
#   // signal that we have completed this scan
#   rt_sem_broadcast(rt_thread_sem);
# }
# 
# int terminate = 0;
#
# int run_eoe(void *args)
# {
#   ec_master_t *master = args;
#   int eoe_processing;
#   int eoe_open;
#   RT_TASK *task;
#   
#   // initialise the thread task
#   if (!(task = rt_thread_init(nam2num("MSTEOE"), 50/*priority*/, 0, SCHED_FIFO, 0x2)))
#   {
#     return -1;
#   }
# 
#   // run
#   while (!terminate)
#   {
#     eoe_processing = 0;
#     
#     // check if any EoE handlers are open
#     if ( ((eoe_open = ecrt_master_eoe_is_open(master)) > 0) &&
#          wait_on_rt_thread() )
#     {
#       // receive any pending datagrams
#       rt_sem_wait(master_sem);
#       ecrt_master_receive(master);
#       rt_sem_signal(master_sem);
#       
#       
#       // process the eoe handlers and see if there's anything to send
#       // or handler processing still pending
#       eoe_processing = ecrt_master_eoe_process(master);
#       
#       
#       // anything to send?
#       if ((eoe_processing & EOE_STH_TO_SEND) == EOE_STH_TO_SEND)
#       {
#         rt_sem_wait(master_sem);
#         ecrt_master_send_ext(master);
#         rt_sem_signal(master_sem);
#       }
#     }
#     
#     if ( unlikely(eoe_open < 0) )
#     {
#       // external eoe processing not currently available
#       // sleep a long period (1s)
#       rt_sleep(1000000000LL);
#     }
#     else if ((eoe_processing & EOE_STH_PENDING) != EOE_STH_PENDING)
#     {
#       // nothing to do at the moment sleep a period (1ms)
#       rt_sleep(1000000);
#     }
#     else
#     {
#       // still got things to do, yield
#       rt_task_yield();
#     }
#   }
#
#   rt_task_delete(task);
# 
#   return 0;
# }
#
# long start_eoe(ec_master_t *master)
# {
#   terminate = 0;
#   return = rt_thread_create(run_eoe, &master, 10000);
# }
#
# void stop_eoe(long thread_id)
# {
#   terminate = 1;
#   rt_thread_join(thread_id);
# }

Have also removed some send_cb and receive_cb calls as they
look like they are creating a loop

diff --git a/include/ecrt.h b/include/ecrt.h
--- a/include/ecrt.h
+++ b/include/ecrt.h
@@ -1074,6 +1074,45 @@
         ec_master_t *master /**< EtherCAT master. */
         );
 
+#if !defined(__KERNEL__) && defined(EC_RTDM) && (EC_EOE)
+
+/** check if there are any open eoe handlers
+ *
+ * used by user space code to process EOE handlers
+ *
+ * \return 1 if any eoe handlers are open, zero if not,
+ *   otherwise a negative error code.
+ */
+int ecrt_master_eoe_is_open(
+        ec_master_t *master /**< EtherCAT master. */
+        );
+
+/** return flag from ecrt_master_eoe_process() to indicate there is
+ * something to send.  if this flag is set call ecrt_master_send_ext()
+ */
+#define EOE_STH_TO_SEND 1
+
+/** return flag from ecrt_master_eoe_process() to indicate there is
+ * something still pending.  if this flag is set yield the process
+ * before starting the cycle again quickly, else sleep for a short time
+ * (e.g. 1ms)
+ */
+
+#define EOE_STH_PENDING 2
+
+/** Check if any EOE handlers are open.
+ *
+ * used by user space code to process EOE handlers
+ *
+ * \return 1 if something to send +
+ *   2 if an eoe handler has something still pending
+ */
+int ecrt_master_eoe_process(
+        ec_master_t *master /**< EtherCAT master. */
+        );
+        
+#endif /* !defined(__KERNEL__) && defined(EC_RTDM) && (EC_EOE) */
+
 #ifdef EC_EOE
 
 /** add an EOE network interface
diff --git a/lib/master.c b/lib/master.c
--- a/lib/master.c
+++ b/lib/master.c
@@ -727,6 +727,56 @@
 
 /****************************************************************************/
 
+#if defined(EC_RTDM) && (EC_EOE)
+
+size_t ecrt_master_send_ext(ec_master_t *master)
+{
+    int ret;
+    size_t sent_bytes = 0;
+
+    ret = ioctl(master->fd, EC_IOCTL_SEND_EXT, &sent_bytes);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        EC_PRINT_ERR("Failed to send ext: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+
+    return sent_bytes;
+}
+
+/****************************************************************************/
+
+int ecrt_master_eoe_is_open(ec_master_t *master)
+{
+    int ret;
+    
+    ret = ioctl(master->fd, EC_IOCTL_EOE_IS_OPEN, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        EC_PRINT_ERR("Failed to check if an eoe is open: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+
+    return ret;
+}
+        
+/****************************************************************************/
+
+int ecrt_master_eoe_process(ec_master_t *master)
+{
+    int ret;
+    
+    ret = ioctl(master->fd, EC_IOCTL_EOE_PROCESS, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        EC_PRINT_ERR("Failed to process eoe handlers: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+
+    return ret;
+}
+
+#endif
+        
+/****************************************************************************/
+
 #ifdef EC_EOE
 
 int ecrt_master_eoe_addif(ec_master_t *master, uint16_t alias, uint16_t posn)
diff --git a/master/ioctl.c b/master/ioctl.c
--- a/master/ioctl.c
+++ b/master/ioctl.c
@@ -1900,6 +1900,50 @@
 
 /*****************************************************************************/
 
+#if defined(EC_RTDM) && defined(EC_EOE)
+
+/** Check if any EOE handlers are open.
+ *
+ * \return 1 if any eoe handlers are open, zero if not,
+ *   otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_eoe_is_open(
+        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;
+    }
+
+    return ec_master_eoe_is_open(master);
+}
+
+/*****************************************************************************/
+
+/** Check if any EOE handlers are open.
+ *
+ * \return 1 if something to send +
+ *   2 if an eoe handler has something still pending
+ */
+static ATTRIBUTES int ec_ioctl_eoe_process(
+        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;
+    }
+
+    return ec_master_eoe_process(master);
+}
+
+#endif
+
+/*****************************************************************************/
+
 /** Create a domain.
  *
  * \return Domain index on success, otherwise a negative error code.
@@ -2280,11 +2324,15 @@
     if (ec_ioctl_lock_down_interruptible(&master->master_sem))
         return -EINTR;
 
+#if defined(EC_RTDM) && defined(EC_EOE)
+    sent_bytes = ecrt_master_send(master);
+#else
     if (master->send_cb != NULL) {
         master->send_cb(master->cb_data);
         sent_bytes = 0;
     } else
         sent_bytes = ecrt_master_send(master);
+#endif
 
     ec_ioctl_lock_up(&master->master_sem);
 
@@ -2316,17 +2364,53 @@
     if (ec_ioctl_lock_down_interruptible(&master->master_sem))
         return -EINTR;
 
+#if defined(EC_RTDM) && defined(EC_EOE)
+    ecrt_master_receive(master);
+#else
     if (master->receive_cb != NULL)
         master->receive_cb(master->cb_data);
     else
         ecrt_master_receive(master);
+#endif
 
     ec_ioctl_lock_up(&master->master_sem);
+
     return 0;
 }
 
 /*****************************************************************************/
 
+#if defined(EC_RTDM) && defined(EC_EOE)
+
+/** Send frames ext.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_send_ext(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    size_t sent_bytes;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    sent_bytes = ecrt_master_send_ext(master);
+
+    if (copy_to_user((void __user *) arg, &sent_bytes, sizeof(sent_bytes))) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+#endif
+
+/*****************************************************************************/
+
 /** Get the master state.
  *
  * \return Zero on success, otherwise a negative error code.
@@ -5308,6 +5392,14 @@
             }
             ret = ec_ioctl_request(master, arg, ctx);
             break;
+#if defined(EC_RTDM) && defined(EC_EOE)
+        case EC_IOCTL_EOE_IS_OPEN:
+            ret = ec_ioctl_eoe_is_open(master, arg, ctx);
+            break;
+        case EC_IOCTL_EOE_PROCESS:
+            ret = ec_ioctl_eoe_process(master, arg, ctx);
+            break;
+#endif
         case EC_IOCTL_CREATE_DOMAIN:
             if (!ctx->writable) {
                 ret = -EPERM;
@@ -5371,6 +5463,15 @@
             }
             ret = ec_ioctl_receive(master, arg, ctx);
             break;
+#if defined(EC_RTDM) && defined(EC_EOE)
+        case  EC_IOCTL_SEND_EXT:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_send_ext(master, arg, ctx);
+            break;
+#endif
         case EC_IOCTL_MASTER_STATE:
             ret = ec_ioctl_master_state(master, arg, ctx);
             break;
diff --git a/master/ioctl.h b/master/ioctl.h
--- a/master/ioctl.h
+++ b/master/ioctl.h
@@ -172,6 +172,12 @@
 #define EC_IOCTL_RT_SLAVE_REQUESTS     EC_IOW(0x6b, uint32_t)
 #define EC_IOCTL_EXEC_SLAVE_REQUESTS    EC_IO(0x6c)
 
+#if defined(EC_RTDM) && (EC_EOE)
+#define EC_IOCTL_EOE_IS_OPEN            EC_IO(0x6d)
+#define EC_IOCTL_EOE_PROCESS            EC_IO(0x6e)
+#define EC_IOCTL_SEND_EXT               EC_IO(0x6f)
+#endif
+
 #ifdef EC_EOE
 #define EC_IOCTL_EOE_ADDIF            EC_IOWR(0x70, ec_ioctl_eoe_if_t)
 #define EC_IOCTL_EOE_DELIF            EC_IOWR(0x71, ec_ioctl_eoe_if_t)
diff --git a/master/master.c b/master/master.c
--- a/master/master.c
+++ b/master/master.c
@@ -1948,12 +1948,13 @@
     }
 
     if (!master->send_cb || !master->receive_cb) {
-        EC_MASTER_WARN(master, "No EoE processing"
-                " because of missing callbacks!\n");
+        EC_MASTER_WARN(master, "EoE External processing"
+                " required!\n");
         return;
     }
 
     EC_MASTER_INFO(master, "Starting EoE thread.\n");
+
     master->eoe_thread = kthread_run(ec_master_eoe_thread, master,
             "EtherCAT-EoE");
     if (IS_ERR(master->eoe_thread)) {
@@ -1985,6 +1986,86 @@
 
 /*****************************************************************************/
 
+#ifdef EC_RTDM
+
+/** Check if any EOE handlers are open.
+ *
+ * \return 1 if any eoe handlers are open, zero if not,
+ *   otherwise a negative error code.
+ */
+int ec_master_eoe_is_open(ec_master_t *master /**< EtherCAT master */)
+{
+    ec_eoe_t *eoe;
+    
+    // check that eoe is not already being processed by the master
+    // and that we can currently process EoE
+    if ( (master->phase != EC_OPERATION) || master->eoe_thread || 
+            !master->rt_slaves_available ) {
+        // protocol not available
+        return -ENOPROTOOPT;
+    }
+
+    ec_lock_down(&master->master_sem);
+    list_for_each_entry(eoe, &master->eoe_handlers, list) {
+        if (ec_eoe_is_open(eoe)) {
+            ec_lock_up(&master->master_sem);
+            return 1;
+        }
+    }
+    ec_lock_up(&master->master_sem);
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Check if any EOE handlers are open.
+ *
+ * \return 1 if something to send +
+ *   2 if an eoe handler has something still pending
+ */
+int ec_master_eoe_process(ec_master_t *master /**< EtherCAT master */)
+{
+    ec_eoe_t *eoe;
+    int sth_to_send = 0;
+    int sth_pending = 0;
+
+    // check that eoe is not already being processed by the master
+    if (master->eoe_thread) {
+        return 0;
+    }
+
+     // actual EoE processing
+    ec_lock_down(&master->master_sem);
+    list_for_each_entry(eoe, &master->eoe_handlers, list) {
+        if ( eoe->slave && 
+             ( (eoe->slave->current_state == EC_SLAVE_STATE_PREOP) ||
+               (eoe->slave->current_state == EC_SLAVE_STATE_SAFEOP) ||
+               (eoe->slave->current_state == EC_SLAVE_STATE_OP) ) ) {
+            ec_eoe_run(eoe);
+            if (eoe->queue_datagram) {
+                sth_to_send = EOE_STH_TO_SEND;
+            }
+            if (!ec_eoe_is_idle(eoe)) {
+                sth_pending = EOE_STH_PENDING;
+            }
+        }
+    }
+
+    if (sth_to_send) {
+        list_for_each_entry(eoe, &master->eoe_handlers, list) {
+            ec_eoe_queue(eoe);
+        }
+    }
+    ec_lock_up(&master->master_sem);
+
+    return sth_to_send + sth_pending;
+}
+
+#endif
+
+/*****************************************************************************/
+
 /** Does the Ethernet over EtherCAT processing.
  */
 static int ec_master_eoe_thread(void *priv_data)
diff --git a/master/master.h b/master/master.h
--- a/master/master.h
+++ b/master/master.h
@@ -118,6 +118,19 @@
  */
 #define EC_EXT_RING_SIZE 32
 
+/** return flag from ecrt_master_eoe_process() to indicate there is
+ * something to send.  if this flag is set call ecrt_master_send_ext()
+ */
+#define EOE_STH_TO_SEND 1
+
+/** return flag from ecrt_master_eoe_process() to indicate there is
+ * something still pending.  if this flag is set yield the process
+ * before starting the cycle again quickly, else sleep for a short time
+ * (e.g. 1ms)
+ */
+
+#define EOE_STH_PENDING 2
+
 /*****************************************************************************/
 
 /** EtherCAT master phase.
@@ -393,6 +406,10 @@
 #ifdef EC_EOE
 uint16_t ec_master_eoe_handler_count(const ec_master_t *);
 const ec_eoe_t *ec_master_get_eoe_handler_const(const ec_master_t *, uint16_t);
+#ifdef EC_RTDM
+int ec_master_eoe_is_open(ec_master_t *);
+int ec_master_eoe_process(ec_master_t *);
+#endif
 #endif
 
 int ec_master_debug_level(ec_master_t *, unsigned int);
