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