Hi,

(I'm writing to both lists, users and dev, because I'm not sure
which one I should use. The description for dev says: "This list is
used for communication between EtherLab developers." which I'm not,
but my previous bug reports in the users list were ignored.)

I noticed there is a stable-1.5 branch now, so I decided to try it.
I found several problems, some of which I could fix, some not.

For reference, I'm using a 2.6.24-16-rtai kernel and an e1000
network interface.

- Including semaphore.h from master/ethernet.h needs a kernel
  version check, as is done in several other files
  (ethercat-1.5-header.patch).

- The e1000 driver has the same problem I reported for 1.4.0
  (http://lists.etherlab.org/pipermail/etherlab-users/2011/001190.html),
  and the same patch fixes it (ethercat-1.5-e1000.patch).

  A similar patch should probably also be applied to the other
  kernel versions of the e1000 driver.

- Also, the problem with the debug interface during RTAI PDO
  transfer still exists
  (http://lists.etherlab.org/pipermail/etherlab-users/2011/001205.html),
  although the behaviour is a little different: It doesn't give a
  "Kernel BUG" anymore, but "Default Trap Handler: vector 6: Suspend
  RT task f8840880" (and the cyclic task indeed gets suspended). But
  the same patch as before fixes it
  (ethercat-1.5-debug-disable.patch).

- "ethercat download" with a string type cuts the input string at
  the first space (but the size is given correctly, so for the rest
  of the string garbage is sent). This is due to the behaviour of
  ">>" and easily fixed using "read"
  (ethercat-1.5-string-download.patch).

- As soon as I try to use EoE I get an error in syslog (usually
  already when I just start the EoE devices the error appears every
  few seconds, but certainly when I do anything with EoE such as
  just "ping"):

    BUG: scheduling while atomic: swapper/0/0x10000100

    Pid: 0, comm: swapper Tainted: GF       (2.6.24-16-rtai #1)
    EFLAGS: 00000246 CPU: 0
    EIP is at default_idle+0x27/0x39
    EAX: 00000000 EBX: 00000000 ECX: 00000000 EDX: 00000000
    ESI: 00000000 EDI: 00000000 EBP: 007c4007 ESP: c033c140
     DS: 0000 ES: 0000 FS: 0000 GS: 0000 SS: 0068
    CR0: 8005003b CR2: 080f6008 CR3: 1f82a000 CR4: 000006d0
    DR0: 00000000 DR1: 00000000 DR2: 00000000 DR3: 00000000
    DR6: ffff0ff0 DR7: 00000400
    i8042_panic_blink+0x0/0x129

  When I comment out (just for debugging) the part from "down" to
  "up" inclusively in ec_eoedev_tx(), the error goes away (but, of
  course, EoE is non-functional). When I just add the "down" and
  "up" statements back in (nothing in between), the error reappears.

  So I suppose that the schedule() that might be called by down()
  actually causes the problem because apparently ec_eoedev_tx() is
  already called from atomic context (via hard_start_xmit), which
  seems to be the case according to a quick Google search. When I
  compared it with 1.4.0 which didn't have this problem, I noticed
  that 1.4.0 used a spinlock rather than a semaphore here. So I
  reverted the code back to a spinlock, and the problem went away
  and EoE became usable. I don't know what was the motivation for
  replacing the spinlock with a semaphore here, but at least in this
  case, it seems to be wrong (ethercat-1.5-eoe.patch).

- examples/mini/ gives the same syslog error ("scheduling while
  atomic") immediately upon "insmod". The error persists if I remove
  everything from cyclic_task() except for a single down() and up()
  call (but disappears if I remove these statements too).

  I see that the cyclic task is registered with add_timer(). Indeed
  according to http://www.makelinux.net/ldd3/chp-7-sect-4, such
  timer callbacks run in atomic context (interrupt context in fact)
  and therefore "Semaphores also must not be used since they can
  sleep."

  A solution might be to also revert the semaphore back to a
  spinlock as it was in 1.4.0. However, I'm not sure if any of the
  other functions called by cyclic_task() can sleep (or do anything
  else that's forbidden in interrupt context). If so, it might be
  easier to avoid the timer callback altogether and convert it to a
  kernel thread with sleeps or so.

  The same probably applies to examples/tty/ which I didn't test and
  possibly to tty/module.c.

- I wonder whether the use of RTAI semaphores in the master
  callbacks in the examples (e.g. examples/rtai/) is safe. (Since my
  own application also uses RTAI, in the same style as those
  examples, this is an important question for me, not just
  hypothetical.) I found this message:
  http://blog.gmane.org/gmane.linux.real-time.rtai/month=20020801
  "Any nonblocking function can be used from Linux, typically
  rt_sem_signal and rt_task_resume". The message is a bit dated, and
  he doesn't strictly say that blocking functions cannot be used
  from Linux (i.e., non-RTAI kernel modules), but it might be
  implied. Therefore the use of rt_sem_wait() in the callbacks might
  be problematic.

  I admit I don't fully understand the differences between normal
  and RTAI semaphores. I gather their function is identical, they
  just interact with different schedulers (Linux vs. RTAI) when
  necessary, i.e. when there is contention. Since the callbacks are
  called from non-RTAI code (namely the EoE kernel task), this would
  lead to problems in this case.

  I thought that a solution would be to use the non-blocking
  rt_sem_wait_if(), but I see that it also accesses RT_CURRENT, so
  it might also be problematic. But I really have problems
  understanding the RTAI documentation (it seems to be written in a
  confusing way to me and often in bad English), so I'm not
  completely sure what's allowed.

  I also thought of a spinlock, but AFAICS it wouldn't work on a
  single-core machine or whenever the cyclic task is scheduled on
  the same CPU as the EoE task.

  A safe alternative then might be to use a simple atomic counter to
  implement our own "semaphore" which never blocks (i.e., only
  provides a "try-lock" method, so the "unlock" method never needs
  to wake up any waiting tasks, so it would be completely
  independent of any scheduler). Since the callbacks are allowed to
  do nothing if inconvenient, this seems to be valid for them. And
  for the cyclic task, well, if the master is locked when the cycle
  should run, we have lost anyway (the t_critical check should
  prevent this from ever happening), so the best we can do then is
  probably to log the problem and skip to the next cycle (or wait a
  fraction of the cycle time and try again). I can try to implement
  this, but first I'd like to know if this is actually needed, or if
  there's a better solution, or if RTAI semaphores are actually safe
  to use this way (if so, I'd appreciate a reference that confirms
  this because so far I don't have this impression).

- Related to this: I see that the example code acquires and releases
  the semaphore several times during one cycle (at least twice, more
  often if the optional checks are done). I'm not sure that's a good
  idea. Even if it doesn't need the master for a moment, I don't
  think we want to allow another task to grab it until the cycle is
  finished. (In general, fine-grained locking is a good idea, of
  course, but here I think the hard-realtime constraints are more
  important.)

  Note that this is not prevented by the t_critical
  check, since t_last_cycle is updated at the beginning of run(). If
  it was updated at the end, which I'd also suggest, it should not
  be possible for another task (via the callbacks) to get
  master_sem, but then it's no problem for the cyclic task to hold
  it for the whole cycle anyway.

  The patch (ethercat-1.5-rtai-lock.patch, to be applied after
  ethercat-1.5-debug-disable.patch) changes these two things,
  without changing the RTAI semaphores yet.

  This and the previous point probably also apply to
  examples/dc_rtai/ which I didn't test.

- In my application I need the ability to read/write arbitrary SDOs
  (from the non-realtime, but kernel-module part of the code, so
  going through the cdev would be awkward; however at a time when
  the master is already activated and running).

  I thought I could just do an
  ecrt_slave_config_create_sdo_request() when needed. I'm not sure
  if this function is supposed to be used while the master is
  running (I couldn't find a statement that forbids it anyway).
  However, there is no corresponding "delete" function, so used-up
  SDO requests would accumulate and leak.

  I see three possible solutions:

  - Implement ecrt_slave_config_delete_sdo_request() or such. Is
    there more to it than basically doing
    "list_del(&req->list, &sc->sdo_requests);" after appropriate
    checks that the request is not busy etc.? And if done, would it
    be possible/reasonable to use
    ecrt_slave_config_create_sdo_request() while the master is
    running?

  - Allow changing the index and subindex of an existing request (so
    I could create some requests on startup and reuse them for
    arbitrary SDOs -- I only need a fixed number of them
    simultaneously). This seems to match the TODO list item: "Change
    SDO index at runtime for SDO request." Is there more to it than
    calling ec_sdo_request_address() (again, after appropriate
    checks)?

  - Implement ecrt_master_sdo_{down,up}load() also for (non-RT)
    kernel access. Of course, this could be implemented simply on
    top of ecrt_slave_config_create_sdo_request() etc. if either of
    the previous two solutions were implemented, but then the master
    has to call ecrt_sdo_request_read() etc. (as in read_sdo() in
    examples/mini/mini.c), so it would have to know about the SDO
    requests which might be a problem in the general case (in my own
    application probably not -- I know which SDO requests I have and
    can let my master know about them).

    However, I see that this is apparently not required for the way
    the cdev does SDO transfers, so I tried to adjust this code for
    in-kernel use and implemented ecrt_master_sdo_{down,up}load() in
    slave_config.c. I'm not sure if it's intended to be used this
    way, and I wonder especially about the usage of
    ec_master_find_slave() (apparently the user-space code uses
    different ways to identify a slave -- although the tool accepts
    alias and position as command-line arguments, it only passes the
    position to the ioctl; however, in the ec_slave_config_t struct
    used in the kernel we have both alias and position; but since
    ec_master_find_slave() accepts alias and position, this might be
    alright). The code is not commented etc., since I'm not yet sure
    if that's the right way to go
    (ethercat-1.5-sdo-up-download.patch).

    When testing it I found that it works most of the time, but
    sometimes I get errors like the following:

    EtherCAT ERROR 0-0: Received upload response for wrong SDO (...)

    EtherCAT ERROR 0-0: Reception of CoE upload response failed: No response.

    EtherCAT ERROR 0-0: Reception of CoE download response failed: No response.

    This might be another case of mailbox contention (I'll talk
    about this in another mail, since it seems to be a major topic
    of its own), this time between the cyclic task's SDO access and
    the new functions called from a normal kernel task (and
    therefore would probably also occur between the former and the
    cdev, since it uses the same mechanism).

PS: Of course, all my patches are released under the GPL, version 2
or any later version, and I hope they will be integrated in future
releases.

Regards,
Frank

-- 
Dipl.-Math. Frank Heckenbach <[email protected]>
Systemprogrammierung, EDV-Beratung
Stubenlohstr. 6, 91052 Erlangen, Deutschland
Tel.: +49-9131-21359
--- etherlabmaster-2ab48cb3a5b4/master/ethernet.h.orig	2011-05-27 17:17:57.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/master/ethernet.h	2011-05-27 17:18:09.000000000 +0200
@@ -39,7 +39,11 @@
 
 #include <linux/list.h>
 #include <linux/netdevice.h>
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
 #include <linux/semaphore.h>
+#else
+#include <asm/semaphore.h>
+#endif
 
 #include "globals.h"
 #include "slave.h"
--- etherlabmaster-2ab48cb3a5b4/devices/e1000/e1000_main-2.6.24-ethercat.c.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/devices/e1000/e1000_main-2.6.24-ethercat.c	2011-05-27 17:32:00.000000000 +0200
@@ -3386,7 +3386,8 @@
 				if (!__pskb_pull_tail(skb, pull_size)) {
 					DPRINTK(DRV, ERR,
 						"__pskb_pull_tail failed.\n");
-					dev_kfree_skb_any(skb);
+					if (!adapter->ecdev)
+						dev_kfree_skb_any(skb);
 					return NETDEV_TX_OK;
 				}
 				len = skb->len - skb->data_len;
@@ -3936,7 +3937,7 @@
 #ifdef CONFIG_E1000_NAPI
 	/* IMS will not auto-mask if INT_ASSERTED is not set, and if it is
 	 * not set, then the adapter didn't send an interrupt */
-	if (unlikely(hw->mac_type >= e1000_82571 &&
+	if (!adapter->ecdev && unlikely(hw->mac_type >= e1000_82571 &&
 	             !(icr & E1000_ICR_INT_ASSERTED)))
 		return IRQ_NONE;
 
--- etherlabmaster-2ab48cb3a5b4/include/ecrt.h.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/include/ecrt.h	2011-05-27 17:48:21.000000000 +0200
@@ -1419,6 +1419,12 @@
         ec_sdo_request_t *req /**< SDO request. */
         );
 
+/** Temporarily disable the debug interface.
+ */
+void ec_debug_disable(
+        int disable /**< 1 to disable, 0 to re-enable. */
+        );
+
 /*****************************************************************************
  * VoE handler methods.
  ****************************************************************************/
--- etherlabmaster-2ab48cb3a5b4/master/debug.c.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/master/debug.c	2011-05-27 17:50:02.000000000 +0200
@@ -44,6 +44,8 @@
 
 /*****************************************************************************/
 
+static int ec_debug_disabled = 0;
+
 // net_device functions
 int ec_dbgdev_open(struct net_device *);
 int ec_dbgdev_stop(struct net_device *);
@@ -172,7 +174,7 @@
 {
     struct sk_buff *skb;
 
-    if (!dbg->opened)
+    if (!dbg->opened || ec_debug_disabled)
         return;
 
     // allocate socket buffer
@@ -195,6 +197,17 @@
     netif_rx(skb);
 }
 
+/*****************************************************************************/
+
+/**
+   Temporarily disable the debug interface.
+*/
+
+void ec_debug_disable(int disable)
+{
+    ec_debug_disabled = disable;
+}
+
 /******************************************************************************
  *  NET_DEVICE functions
  *****************************************************************************/
@@ -256,3 +269,11 @@
 }
 
 /*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ec_debug_disable);
+
+/** \endcond */
+
+/*****************************************************************************/
--- etherlabmaster-2ab48cb3a5b4/examples/rtai/rtai_sample.c.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/examples/rtai/rtai_sample.c	2011-05-27 17:51:16.000000000 +0200
@@ -207,8 +207,12 @@
 
         // receive process data
         rt_sem_wait(&master_sem);
+        // disable the debug interface which is not RTAI-safe
+        ec_debug_disable(1);
         ecrt_master_receive(master);
         ecrt_domain_process(domain1);
+        // re-enable the debug interface
+        ec_debug_disable(0);
         rt_sem_signal(&master_sem);
 
         // check process data state (optional)
@@ -233,8 +237,12 @@
         EC_WRITE_U8(domain1_pd + off_dig_out, blink ? 0x06 : 0x09);
 
         rt_sem_wait(&master_sem);
+        // disable the debug interface which is not RTAI-safe
+        ec_debug_disable(1);
         ecrt_domain_queue(domain1);
         ecrt_master_send(master);
+        // re-enable the debug interface
+        ec_debug_disable(0);
         rt_sem_signal(&master_sem);
 
         rt_task_wait_period();
@@ -380,6 +388,8 @@
 
     rt_task_delete(&task);
     stop_rt_timer();
+    // re-enable the debug interface in case the task was deleted while it was disabled
+    ec_debug_disable(0);
     ecrt_release_master(master);
     rt_sem_delete(&master_sem);
 
--- etherlabmaster-2ab48cb3a5b4/tool/DataTypeHandler.cpp.orig	2011-06-04 01:18:00.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/tool/DataTypeHandler.cpp	2011-06-04 01:17:44.000000000 +0200
@@ -191,7 +191,7 @@
                 err << "String too large";
                 throw SizeException(err.str());
             }
-            str >> (char *) target;
+            str.read((char *) target, dataSize);
             break;
         case 0x0011: // double
             {
--- etherlabmaster-2ab48cb3a5b4/master/ethernet.h.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/master/ethernet.h	2011-05-31 14:46:04.000000000 +0200
@@ -92,7 +92,7 @@
     unsigned int tx_queue_size; /**< Transmit queue size. */
     unsigned int tx_queue_active; /**< kernel netif queue started */
     unsigned int tx_queued_frames; /**< number of frames in the queue */
-    struct semaphore tx_queue_sem; /**< Semaphore for the send queue. */
+    spinlock_t tx_queue_lock; /**< spinlock for the send queue */
     ec_eoe_frame_t *tx_frame; /**< current TX frame */
     uint8_t tx_frame_number; /**< number of the transmitted frame */
     uint8_t tx_fragment_number; /**< number of the fragment */
--- etherlabmaster-2ab48cb3a5b4/master/ethernet.c.orig	2011-05-12 09:00:59.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/master/ethernet.c	2011-05-31 14:47:26.000000000 +0200
@@ -122,7 +122,7 @@
     eoe->tx_queue_size = EC_EOE_TX_QUEUE_SIZE;
     eoe->tx_queued_frames = 0;
 
-    sema_init(&eoe->tx_queue_sem, 1);
+    eoe->tx_queue_lock = SPIN_LOCK_UNLOCKED;
     eoe->tx_frame_number = 0xFF;
     memset(&eoe->stats, 0, sizeof(struct net_device_stats));
 
@@ -231,7 +231,7 @@
 {
     ec_eoe_frame_t *frame, *next;
 
-    down(&eoe->tx_queue_sem);
+    spin_lock_bh(&eoe->tx_queue_lock);
 
     list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) {
         list_del(&frame->queue);
@@ -240,7 +240,7 @@
     }
     eoe->tx_queued_frames = 0;
 
-    up(&eoe->tx_queue_sem);
+    spin_unlock_bh(&eoe->tx_queue_lock);
 }
 
 /*****************************************************************************/
@@ -620,10 +620,10 @@
         return;
     }
 
-    down(&eoe->tx_queue_sem);
+    spin_lock_bh(&eoe->tx_queue_lock);
 
     if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
-        up(&eoe->tx_queue_sem);
+        spin_unlock_bh(&eoe->tx_queue_lock);
         eoe->tx_idle = 1;
         // no data available.
         // start a new receive immediately.
@@ -644,7 +644,7 @@
     }
 
     eoe->tx_queued_frames--;
-    up(&eoe->tx_queue_sem);
+    spin_unlock_bh(&eoe->tx_queue_lock);
 
     eoe->tx_idle = 0;
 
@@ -812,14 +812,14 @@
 
     frame->skb = skb;
 
-    down(&eoe->tx_queue_sem);
+    spin_lock_bh(&eoe->tx_queue_lock);
     list_add_tail(&frame->queue, &eoe->tx_queue);
     eoe->tx_queued_frames++;
     if (eoe->tx_queued_frames == eoe->tx_queue_size) {
         netif_stop_queue(dev);
         eoe->tx_queue_active = 0;
     }
-    up(&eoe->tx_queue_sem);
+    spin_unlock_bh(&eoe->tx_queue_lock);
 
 #if EOE_DEBUG_LEVEL >= 2
     EC_SLAVE_DBG(eoe->slave, 0, "EoE %s TX queued frame"
--- etherlabmaster-2ab48cb3a5b4/examples/rtai/rtai_sample.c.orig	2011-06-06 12:34:04.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/examples/rtai/rtai_sample.c	2011-06-18 05:21:19.000000000 +0200
@@ -141,13 +141,11 @@
 
 /*****************************************************************************/
 
+// Caller must hold master_sem
 void check_domain1_state(void)
 {
     ec_domain_state_t ds;
-
-    rt_sem_wait(&master_sem);
     ecrt_domain_state(domain1, &ds);
-    rt_sem_signal(&master_sem);
 
     if (ds.working_counter != domain1_state.working_counter)
         printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);
@@ -159,13 +157,11 @@
 
 /*****************************************************************************/
 
+// Caller must hold master_sem
 void check_master_state(void)
 {
     ec_master_state_t ms;
-
-    rt_sem_wait(&master_sem);
     ecrt_master_state(master, &ms);
-    rt_sem_signal(&master_sem);
 
     if (ms.slaves_responding != master_state.slaves_responding)
         printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
@@ -179,13 +175,11 @@
 
 /*****************************************************************************/
 
+// Caller must hold master_sem
 void check_slave_config_states(void)
 {
     ec_slave_config_state_t s;
-
-    rt_sem_wait(&master_sem);
     ecrt_slave_config_state(sc_ana_in, &s);
-    rt_sem_signal(&master_sem);
 
     if (s.al_state != sc_ana_in_state.al_state)
         printk(KERN_INFO PFX "AnaIn: State 0x%02X.\n", s.al_state);
@@ -203,17 +197,12 @@
 void run(long data)
 {
     while (1) {
-        t_last_cycle = get_cycles();
-
         // receive process data
         rt_sem_wait(&master_sem);
         // disable the debug interface which is not RTAI-safe
         ec_debug_disable(1);
         ecrt_master_receive(master);
         ecrt_domain_process(domain1);
-        // re-enable the debug interface
-        ec_debug_disable(0);
-        rt_sem_signal(&master_sem);
 
         // check process data state (optional)
         check_domain1_state();
@@ -236,15 +225,13 @@
         // write process data
         EC_WRITE_U8(domain1_pd + off_dig_out, blink ? 0x06 : 0x09);
 
-        rt_sem_wait(&master_sem);
-        // disable the debug interface which is not RTAI-safe
-        ec_debug_disable(1);
         ecrt_domain_queue(domain1);
         ecrt_master_send(master);
         // re-enable the debug interface
         ec_debug_disable(0);
         rt_sem_signal(&master_sem);
 
+        t_last_cycle = get_cycles();
         rt_task_wait_period();
     }
 }
--- etherlabmaster-2ab48cb3a5b4/include/ecrt.h.orig	2011-06-06 00:17:21.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/include/ecrt.h	2011-06-06 00:17:23.000000000 +0200
@@ -675,6 +675,17 @@
         uint32_t *abort_code /**< Abort code of the SDO upload. */
         );
 
+#else
+
+int ecrt_master_sdo_download(ec_slave_config_t *sc,
+        uint16_t index, uint8_t subindex,
+        uint8_t *data, size_t data_size);
+
+int ecrt_master_sdo_upload(ec_slave_config_t *sc,
+        uint16_t index, uint8_t subindex,
+        uint8_t *target, size_t target_size, size_t *data_size);
+
+
 #endif /* #ifndef __KERNEL__ */
 
 /** Executes an SoE write request.
--- etherlabmaster-2ab48cb3a5b4/master/slave_config.c.orig	2011-06-06 00:18:24.000000000 +0200
+++ etherlabmaster-2ab48cb3a5b4/master/slave_config.c	2011-06-06 00:23:00.000000000 +0200
@@ -1059,3 +1059,152 @@
 /** \endcond */
 
 /*****************************************************************************/
+
+int ecrt_master_sdo_download(ec_slave_config_t *sc,
+        uint16_t index, uint8_t subindex,
+        uint8_t *data, size_t data_size)
+{
+    ec_master_sdo_request_t request;
+    int retval;
+
+    if (!data_size) {
+        EC_MASTER_ERR(sc->master, "Zero data size!\n");
+        return -EINVAL;
+    }
+
+    ec_sdo_request_init(&request.req);
+    ec_sdo_request_address(&request.req, index, subindex);
+    if (ec_sdo_request_alloc(&request.req, data_size)) {
+        ec_sdo_request_clear(&request.req);
+        return -ENOMEM;
+    }
+    memcpy(request.req.data, data, data_size);
+    request.req.data_size = data_size;
+    ecrt_sdo_request_write(&request.req);
+
+    if (down_interruptible(&sc->master->master_sem))
+        return -EINTR;
+
+    if (!(request.slave = ec_master_find_slave(
+                    sc->master, sc->alias, sc->position))) {
+        up(&sc->master->master_sem);
+        EC_MASTER_ERR(sc->master, "Slave %u:%u does not exist!\n",
+                sc->alias, sc->position);
+        ec_sdo_request_clear(&request.req);
+        return -EINVAL;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Schedule SDO download request.\n");
+
+    // schedule request.
+    list_add_tail(&request.list, &request.slave->slave_sdo_requests);
+
+    up(&sc->master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->sdo_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&sc->master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            list_del(&request.list);
+            up(&sc->master->master_sem);
+            ec_sdo_request_clear(&request.req);
+            return -EINTR;
+        }
+        // request already processing: interrupt not possible.
+        up(&sc->master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->sdo_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    EC_SLAVE_DBG(request.slave, 1, "Finished SDO download request.\n");
+
+    if (request.req.state == EC_INT_REQUEST_SUCCESS) {
+        retval = 0;
+    } else if (request.req.errno) {
+        retval = -request.req.errno;
+    } else {
+        retval = -EIO;
+    }
+
+    ec_sdo_request_clear(&request.req);
+    return retval;
+}
+
+int ecrt_master_sdo_upload(ec_slave_config_t *sc,
+        uint16_t index, uint8_t subindex,
+        uint8_t *target, size_t target_size, size_t *data_size)
+{
+    ec_master_sdo_request_t request;
+    int retval;
+
+    ec_sdo_request_init(&request.req);
+    ec_sdo_request_address(&request.req, index, subindex);
+    ecrt_sdo_request_read(&request.req);
+
+    if (down_interruptible(&sc->master->master_sem))
+        return -EINTR;
+
+    if (!(request.slave = ec_master_find_slave(
+                    sc->master, sc->alias, sc->position))) {
+        up(&sc->master->master_sem);
+        EC_MASTER_ERR(sc->master, "Slave %u:%u does not exist!\n",
+                sc->alias, sc->position);
+        ec_sdo_request_clear(&request.req);
+        return -EINVAL;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Schedule SDO upload request.\n");
+
+    // schedule request.
+    list_add_tail(&request.list, &request.slave->slave_sdo_requests);
+
+    up(&sc->master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->sdo_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&sc->master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            list_del(&request.list);
+            up(&sc->master->master_sem);
+            ec_sdo_request_clear(&request.req);
+            return -EINTR;
+        }
+        // request already processing: interrupt not possible.
+        up(&sc->master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->sdo_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    EC_SLAVE_DBG(request.slave, 1, "Finished SDO upload request.\n");
+
+    if (request.req.state != EC_INT_REQUEST_SUCCESS) {
+        *data_size = 0;
+        if (request.req.errno) {
+            retval = -request.req.errno;
+        } else {
+            retval = -EIO;
+        }
+    } else {
+        if (request.req.data_size > target_size) {
+            EC_MASTER_ERR(sc->master, "Buffer too small.\n");
+            ec_sdo_request_clear(&request.req);
+            return -EOVERFLOW;
+        }        *data_size = request.req.data_size;
+        memcpy(target, request.req.data, *data_size);
+        retval = 0;
+    }
+
+    ec_sdo_request_clear(&request.req);
+    return retval;
+}
+
+EXPORT_SYMBOL(ecrt_master_sdo_download);
+EXPORT_SYMBOL(ecrt_master_sdo_upload);
_______________________________________________
etherlab-dev mailing list
[email protected]
http://lists.etherlab.org/mailman/listinfo/etherlab-dev

Reply via email to