Merged redundancy branch to stable-1.5.
--- a/NEWS Thu Sep 06 14:40:10 2012 +0200
+++ b/NEWS Thu Sep 06 18:28:57 2012 +0200
@@ -37,7 +37,7 @@
- Added e100 driver for 2.6.29, thanks to Andre Puschmann.
- Added e100 driver for 2.6.31.
- Added e100 driver for 2.6.32.
- - Added e100 driver for 2.6.33, thanks for J. Kunz.
+ - Added e100 driver for 2.6.33, thanks to J. Kunz.
- Added e100 driver for 2.6.37.
- Added e100 driver for 3.0.
* Added 8139too driver for kernels 2.6.25 (F. Pose), 2.6.26 (M. Luescher),
@@ -90,7 +90,7 @@
supports ranges like '0,3,8-10'.
* A sync manager is always enabled, if it contains registered process data.
* Added a configuration switch --enable-wildcards to use 0xffffffff as a
-* wildcard for vendor ID and product code.
+ wildcard for vendor ID and product code.
* Added support for systemd.
Changes in 1.4.0:
--- a/include/ecrt.h Thu Sep 06 14:40:10 2012 +0200
+++ b/include/ecrt.h Thu Sep 06 18:28:57 2012 +0200
@@ -39,6 +39,14 @@
* request a master, to map process data, to communicate with slaves via CoE
* and to configure and activate the bus.
*
+ * Changed since 1.5:
+ *
+ * - Added redundancy_active flag to ec_domain_state_t.
+ * - Added ecrt_master_link_state() method and ec_master_link_state_t to query
+ * the state of a redundant link.
+ * - Added the EC_HAVE_REDUNDANCY define, to check, if the interface contains
+ * redundancy features.
+ *
* Changes in version 1.5:
*
* - Added the distributed clocks feature and the respective method
@@ -119,6 +127,17 @@
*/
#define ECRT_VERSION_MAGIC ECRT_VERSION(ECRT_VER_MAJOR, ECRT_VER_MINOR)
+/******************************************************************************
+ * Feature flags
+ *****************************************************************************/
+
+/** Defined, if the redundancy features are available.
+ *
+ * I. e. if the \a redundancy_active flag in ec_domain_state_t and the
+ * ecrt_master_link_state() method are available.
+ */
+#define EC_HAVE_REDUNDANCY
+
/*****************************************************************************/
/** End of list marker.
@@ -179,7 +198,8 @@
* \see ecrt_master_state().
*/
typedef struct {
- unsigned int slaves_responding; /**< Number of slaves in the bus. */
+ unsigned int slaves_responding; /**< Sum of responding slaves on all
+ Ethernet devices. */
unsigned int al_states : 4; /**< Application-layer states of all slaves.
The states are coded in the lower 4 bits.
If a bit is set, it means that at least one
@@ -189,11 +209,36 @@
- Bit 1: \a PREOP
- Bit 2: \a SAFEOP
- Bit 3: \a OP */
- unsigned int link_up : 1; /**< \a true, if the network link is up. */
+ unsigned int link_up : 1; /**< \a true, if at least one Ethernet link is
+ up. */
} ec_master_state_t;
/*****************************************************************************/
+/** Redundant link state.
+ *
+ * This is used for the output parameter of ecrt_master_link_state().
+ *
+ * \see ecrt_master_link_state().
+ */
+typedef struct {
+ unsigned int slaves_responding; /**< Sum of responding slaves on the given
+ link. */
+ unsigned int al_states : 4; /**< Application-layer states of the slaves on
+ the given link. The states are coded in the
+ lower 4 bits. If a bit is set, it means
+ that at least one slave in the bus is in the
+ corresponding state:
+ - Bit 0: \a INIT
+ - Bit 1: \a PREOP
+ - Bit 2: \a SAFEOP
+ - Bit 3: \a OP */
+ unsigned int link_up : 1; /**< \a true, if the given Ethernet link is up.
+ */
+} ec_master_link_state_t;
+
+/*****************************************************************************/
+
/** Slave configuration state.
*
* This is used as an output parameter of ecrt_slave_config_state().
@@ -304,6 +349,7 @@
typedef struct {
unsigned int working_counter; /**< Value of the last working counter. */
ec_wc_state_t wc_state; /**< Working counter interpretation. */
+ unsigned int redundancy_active; /**< Redundant link is in use. */
} ec_domain_state_t;
/*****************************************************************************/
@@ -848,12 +894,29 @@
/** Reads the current master state.
*
* Stores the master state information in the given \a state structure.
+ *
+ * This method returns a global state. For the link-specific states in a
+ * redundant bus topology, use the ecrt_master_link_state() method.
*/
void ecrt_master_state(
const ec_master_t *master, /**< EtherCAT master. */
ec_master_state_t *state /**< Structure to store the information. */
);
+/** Reads the current state of a redundant link.
+ *
+ * Stores the link state information in the given \a state structure.
+ *
+ * \return Zero on success, otherwise negative error code.
+ */
+int ecrt_master_link_state(
+ const ec_master_t *master, /**< EtherCAT master. */
+ unsigned int dev_idx, /**< Index of the device (0 = main device, 1 =
+ first backup device, ...). */
+ ec_master_link_state_t *state /**< Structure to store the information.
+ */
+ );
+
/** Sets the application time.
*
* The master has to know the application's time when operating slaves with
--- a/lib/master.c Thu Sep 06 14:40:10 2012 +0200
+++ b/lib/master.c Thu Sep 06 18:28:57 2012 +0200
@@ -256,8 +256,9 @@
{
ec_ioctl_slave_sync_t data;
- if (sync_index >= EC_MAX_SYNC_MANAGERS)
+ if (sync_index >= EC_MAX_SYNC_MANAGERS) {
return -ENOENT;
+ }
memset(&data, 0x00, sizeof(ec_ioctl_slave_sync_t));
data.slave_position = slave_position;
@@ -552,6 +553,21 @@
/*****************************************************************************/
+int ecrt_master_link_state(const ec_master_t *master, unsigned int dev_idx,
+ ec_master_link_state_t *state)
+{
+ ec_ioctl_link_state_t io;
+
+ io.dev_idx = dev_idx;
+ io.state = state;
+ if (ioctl(master->fd, EC_IOCTL_MASTER_LINK_STATE, &io) == -1) {
+ fprintf(stderr, "Failed to get link state: %s\n", strerror(errno));
+ return -errno;
+ }
+}
+
+/*****************************************************************************/
+
void ecrt_master_application_time(ec_master_t *master, uint64_t app_time)
{
ec_ioctl_app_time_t data;
--- a/master/Kbuild.in Thu Sep 06 14:40:10 2012 +0200
+++ b/master/Kbuild.in Thu Sep 06 18:28:57 2012 +0200
@@ -36,6 +36,7 @@
ec_master-objs := \
cdev.o \
datagram.o \
+ datagram_pair.o \
device.o \
domain.o \
fmmu_config.o \
--- a/master/Makefile.am Thu Sep 06 14:40:10 2012 +0200
+++ b/master/Makefile.am Thu Sep 06 18:28:57 2012 +0200
@@ -31,6 +31,7 @@
noinst_HEADERS = \
cdev.c cdev.h \
datagram.c datagram.h \
+ datagram_pair.c datagram_pair.h \
debug.c debug.h \
device.c device.h \
domain.c domain.h \
--- a/master/cdev.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/cdev.c Thu Sep 06 18:28:57 2012 +0200
@@ -190,10 +190,11 @@
)
{
ec_ioctl_master_t data;
- unsigned int i;
+ unsigned int i, j;
if (down_interruptible(&master->master_sem))
return -EINTR;
+
data.slave_count = master->slave_count;
data.config_count = ec_master_config_count(master);
data.domain_count = ec_master_domain_count(master);
@@ -203,49 +204,55 @@
data.phase = (uint8_t) master->phase;
data.active = (uint8_t) master->active;
data.scan_busy = master->scan_busy;
+
up(&master->master_sem);
if (down_interruptible(&master->device_sem))
return -EINTR;
- if (master->main_device.dev) {
- memcpy(data.devices[0].address,
- master->main_device.dev->dev_addr, ETH_ALEN);
- } else {
- memcpy(data.devices[0].address, master->main_mac, ETH_ALEN);
- }
- data.devices[0].attached = master->main_device.dev ? 1 : 0;
- data.devices[0].link_state = master->main_device.link_state ? 1 : 0;
- data.devices[0].tx_count = master->main_device.tx_count;
- data.devices[0].rx_count = master->main_device.rx_count;
- data.devices[0].tx_bytes = master->main_device.tx_bytes;
- data.devices[0].tx_errors = master->main_device.tx_errors;
+ for (i = 0; i < EC_NUM_DEVICES; i++) {
+ ec_device_t *device = &master->devices[i];
+
+ if (device->dev) {
+ memcpy(data.devices[i].address,
+ device->dev->dev_addr, ETH_ALEN);
+ } else {
+ memcpy(data.devices[i].address, master->macs[i], ETH_ALEN);
+ }
+ data.devices[i].attached = device->dev ? 1 : 0;
+ data.devices[i].link_state = device->link_state ? 1 : 0;
+ data.devices[i].tx_count = device->tx_count;
+ data.devices[i].rx_count = device->rx_count;
+ data.devices[i].tx_bytes = device->tx_bytes;
+ data.devices[i].rx_bytes = device->rx_bytes;
+ data.devices[i].tx_errors = device->tx_errors;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ data.devices[i].tx_frame_rates[j] =
+ device->tx_frame_rates[j];
+ data.devices[i].rx_frame_rates[j] =
+ device->rx_frame_rates[j];
+ data.devices[i].tx_byte_rates[j] =
+ device->tx_byte_rates[j];
+ data.devices[i].rx_byte_rates[j] =
+ device->rx_byte_rates[j];
+ }
+ }
+
+ data.tx_count = master->device_stats.tx_count;
+ data.rx_count = master->device_stats.rx_count;
+ data.tx_bytes = master->device_stats.tx_bytes;
+ data.rx_bytes = master->device_stats.rx_bytes;
for (i = 0; i < EC_RATE_COUNT; i++) {
- data.devices[0].tx_frame_rates[i] =
- master->main_device.tx_frame_rates[i];
- data.devices[0].tx_byte_rates[i] =
- master->main_device.tx_byte_rates[i];
- data.devices[0].loss_rates[i] = master->main_device.loss_rates[i];
- }
-
- if (master->backup_device.dev) {
- memcpy(data.devices[1].address,
- master->backup_device.dev->dev_addr, ETH_ALEN);
- } else {
- memcpy(data.devices[1].address, master->backup_mac, ETH_ALEN);
- }
- data.devices[1].attached = master->backup_device.dev ? 1 : 0;
- data.devices[1].link_state = master->backup_device.link_state ? 1 : 0;
- data.devices[1].tx_count = master->backup_device.tx_count;
- data.devices[1].rx_count = master->backup_device.rx_count;
- data.devices[1].tx_bytes = master->backup_device.tx_bytes;
- data.devices[1].tx_errors = master->backup_device.tx_errors;
- for (i = 0; i < EC_RATE_COUNT; i++) {
- data.devices[1].tx_frame_rates[i] =
- master->backup_device.tx_frame_rates[i];
- data.devices[1].tx_byte_rates[i] =
- master->backup_device.tx_byte_rates[i];
- data.devices[1].loss_rates[i] = master->backup_device.loss_rates[i];
+ data.tx_frame_rates[i] =
+ master->device_stats.tx_frame_rates[i];
+ data.rx_frame_rates[i] =
+ master->device_stats.rx_frame_rates[i];
+ data.tx_byte_rates[i] =
+ master->device_stats.tx_byte_rates[i];
+ data.rx_byte_rates[i] =
+ master->device_stats.rx_byte_rates[i];
+ data.loss_rates[i] =
+ master->device_stats.loss_rates[i];
}
up(&master->device_sem);
@@ -287,6 +294,7 @@
return -EINVAL;
}
+ data.device_index = slave->device_index;
data.vendor_id = slave->sii.vendor_id;
data.product_code = slave->sii.product_code;
data.revision_number = slave->sii.revision_number;
@@ -530,6 +538,7 @@
{
ec_ioctl_domain_t data;
const ec_domain_t *domain;
+ unsigned int dev_idx;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT;
@@ -546,7 +555,9 @@
data.data_size = domain->data_size;
data.logical_base_address = domain->logical_base_address;
- data.working_counter = domain->working_counter;
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ data.working_counter[dev_idx] = domain->working_counter[dev_idx];
+ }
data.expected_working_counter = domain->expected_working_counter;
data.fmmu_count = ec_domain_fmmu_count(domain);
@@ -1818,6 +1829,40 @@
/** Get the master state.
*/
+int ec_cdev_ioctl_master_link_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ unsigned long arg, /**< ioctl() argument. */
+ ec_cdev_priv_t *priv /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_link_state_t ioctl;
+ ec_master_link_state_t state;
+ int ret;
+
+ if (unlikely(!priv->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ ret = ecrt_master_link_state(master, ioctl.dev_idx, &state);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) ioctl.state, &state, sizeof(state))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the master state.
+ */
int ec_cdev_ioctl_app_time(
ec_master_t *master, /**< EtherCAT master. */
unsigned long arg, /**< ioctl() argument. */
@@ -3618,6 +3663,9 @@
case EC_IOCTL_MASTER_STATE:
ret = ec_cdev_ioctl_master_state(master, arg, priv);
break;
+ case EC_IOCTL_MASTER_LINK_STATE:
+ ret = ec_cdev_ioctl_master_link_state(master, arg, priv);
+ break;
case EC_IOCTL_APP_TIME:
if (!(filp->f_mode & FMODE_WRITE)) {
ret = -EPERM;
--- a/master/datagram.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/datagram.c Thu Sep 06 18:28:57 2012 +0200
@@ -88,6 +88,7 @@
void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram. */)
{
INIT_LIST_HEAD(&datagram->queue); // mark as unqueued
+ datagram->device_index = EC_DEVICE_MAIN;
datagram->type = EC_DATAGRAM_NONE;
memset(datagram->address, 0x00, EC_ADDR_LEN);
datagram->data = NULL;
@@ -427,12 +428,69 @@
/** Initializes an EtherCAT LRD datagram.
*
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrd(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRD;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LWR datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lwr(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LWR;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRW;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRD datagram with external memory.
+ *
* \attention It is assumed, that the external memory is at least \a data_size
* bytes large.
*
* \return Return value of ec_datagram_prealloc().
*/
-int ec_datagram_lrd(
+int ec_datagram_lrd_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */
@@ -450,14 +508,14 @@
/*****************************************************************************/
-/** Initializes an EtherCAT LWR datagram.
+/** Initializes an EtherCAT LWR datagram with external memory.
*
* \attention It is assumed, that the external memory is at least \a data_size
* bytes large.
*
* \return Return value of ec_datagram_prealloc().
*/
-int ec_datagram_lwr(
+int ec_datagram_lwr_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */
@@ -475,14 +533,14 @@
/*****************************************************************************/
-/** Initializes an EtherCAT LRW datagram.
+/** Initializes an EtherCAT LRW datagram with external memory.
*
* \attention It is assumed, that the external memory is at least \a data_size
* bytes large.
*
* \return Return value of ec_datagram_prealloc().
*/
-int ec_datagram_lrw(
+int ec_datagram_lrw_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */
--- a/master/datagram.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/datagram.h Thu Sep 06 18:28:57 2012 +0200
@@ -85,9 +85,10 @@
/** EtherCAT datagram.
*/
typedef struct {
- struct list_head list; /**< Needed by domain datagram lists. */
struct list_head queue; /**< Master datagram queue item. */
struct list_head sent; /**< Master list item for sent datagrams. */
+ ec_device_index_t device_index; /**< Device via which the datagram shall
+ be / was sent. */
ec_datagram_type_t type; /**< Datagram type (APRD, BWR, etc.). */
uint8_t address[EC_ADDR_LEN]; /**< Recipient address. */
uint8_t *data; /**< Datagram payload. */
@@ -130,9 +131,12 @@
int ec_datagram_brd(ec_datagram_t *, uint16_t, size_t);
int ec_datagram_bwr(ec_datagram_t *, uint16_t, size_t);
int ec_datagram_brw(ec_datagram_t *, uint16_t, size_t);
-int ec_datagram_lrd(ec_datagram_t *, uint32_t, size_t, uint8_t *);
-int ec_datagram_lwr(ec_datagram_t *, uint32_t, size_t, uint8_t *);
-int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+int ec_datagram_lrd(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lwr(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lrd_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+int ec_datagram_lwr_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+int ec_datagram_lrw_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
void ec_datagram_print_state(const ec_datagram_t *);
void ec_datagram_print_wc_error(const ec_datagram_t *);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/datagram_pair.c Thu Sep 06 18:28:57 2012 +0200
@@ -0,0 +1,173 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * The IgH EtherCAT Master is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * ---
+ *
+ * The license mentioned above concerns the source code only. Using the
+ * EtherCAT technology and brand is only permitted in compliance with the
+ * industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT datagram pair methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "master.h"
+#include "datagram_pair.h"
+
+/*****************************************************************************/
+
+/** Datagram pair constructor.
+ */
+int ec_datagram_pair_init(
+ ec_datagram_pair_t *pair, /**< Datagram pair. */
+ ec_domain_t *domain, /**< Parent domain. */
+ uint32_t logical_offset,
+ uint8_t *data,
+ size_t data_size, /**< Data size. */
+ const unsigned int used[] /**< input/output use count. */
+ )
+{
+ ec_device_index_t dev_idx;
+ int ret;
+
+ INIT_LIST_HEAD(&pair->list);
+ pair->domain = domain;
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_datagram_init(&pair->datagrams[dev_idx]);
+ snprintf(pair->datagrams[dev_idx].name,
+ EC_DATAGRAM_NAME_SIZE, "domain%u-%u-%s", domain->index,
+ logical_offset, ec_device_names[dev_idx]);
+ pair->datagrams[dev_idx].device_index = dev_idx;
+ }
+
+ pair->expected_working_counter = 0U;
+
+ /* backup datagram has its own memory */
+ ret = ec_datagram_prealloc(&pair->datagrams[EC_DEVICE_BACKUP],
+ data_size);
+ if (ret) {
+ goto out_datagrams;
+ }
+
+ if (!(pair->send_buffer = kmalloc(data_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(domain->master,
+ "Failed to allocate domain send buffer!\n");
+ ret = -ENOMEM;
+ goto out_datagrams;
+ }
+
+ /* The ec_datagram_lxx() calls below can not fail, because either the
+ * datagram has external memory or it is preallocated. */
+
+ if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs
+ ec_datagram_lrw_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+ ec_datagram_lrw(&pair->datagrams[EC_DEVICE_BACKUP],
+ logical_offset, data_size);
+
+ // If LRW is used, output FMMUs increment the working counter by 2,
+ // while input FMMUs increment it by 1.
+ pair->expected_working_counter =
+ used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT];
+ } else if (used[EC_DIR_OUTPUT]) { // outputs only
+ ec_datagram_lwr_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+ ec_datagram_lwr(&pair->datagrams[EC_DEVICE_BACKUP],
+ logical_offset, data_size);
+
+ pair->expected_working_counter = used[EC_DIR_OUTPUT];
+ } else { // inputs only (or nothing)
+ ec_datagram_lrd_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+ ec_datagram_lrd(&pair->datagrams[EC_DEVICE_BACKUP],
+ logical_offset, data_size);
+
+ pair->expected_working_counter = used[EC_DIR_INPUT];
+ }
+
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_datagram_zero(&pair->datagrams[dev_idx]);
+ }
+
+ return 0;
+
+out_datagrams:
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_datagram_clear(&pair->datagrams[dev_idx]);
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Datagram pair destructor.
+ */
+void ec_datagram_pair_clear(
+ ec_datagram_pair_t *pair /**< Datagram pair. */
+ )
+{
+ unsigned int dev_idx;
+
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_datagram_clear(&pair->datagrams[dev_idx]);
+ }
+
+ if (pair->send_buffer) {
+ kfree(pair->send_buffer);
+ }
+}
+
+/*****************************************************************************/
+
+/** Process received data.
+ */
+uint16_t ec_datagram_pair_process(
+ ec_datagram_pair_t *pair, /**< Datagram pair. */
+ uint16_t wc_sum[EC_NUM_DEVICES] /**< Working counter sums. */
+ )
+{
+ unsigned int dev_idx;
+ uint16_t pair_wc = 0;
+
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_datagram_t *datagram = &pair->datagrams[dev_idx];
+
+ ec_datagram_output_stats(datagram);
+
+ if (datagram->state == EC_DATAGRAM_RECEIVED) {
+ pair_wc += datagram->working_counter;
+ wc_sum[dev_idx] += datagram->working_counter;
+ }
+ }
+
+ return pair_wc;
+}
+
+/*****************************************************************************/
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/master/datagram_pair.h Thu Sep 06 18:28:57 2012 +0200
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT Master.
+ *
+ * The IgH EtherCAT Master is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ * The IgH EtherCAT Master is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
+ * Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with the IgH EtherCAT Master; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * ---
+ *
+ * The license mentioned above concerns the source code only. Using the
+ * EtherCAT technology and brand is only permitted in compliance with the
+ * industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT datagram pair structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DATAGRAM_PAIR_H__
+#define __EC_DATAGRAM_PAIR_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "datagram.h"
+
+/*****************************************************************************/
+
+/** Domain datagram pair.
+ */
+typedef struct {
+ struct list_head list; /**< List header. */
+ ec_domain_t *domain;
+ ec_datagram_t datagrams[EC_NUM_DEVICES]; /**< Main and backup datagram.
+ */
+ uint8_t *send_buffer;
+ unsigned int expected_working_counter; /**< Expectord working conter. */
+} ec_datagram_pair_t;
+
+/*****************************************************************************/
+
+int ec_datagram_pair_init(ec_datagram_pair_t *, ec_domain_t *, uint32_t,
+ uint8_t *, size_t, const unsigned int []);
+void ec_datagram_pair_clear(ec_datagram_pair_t *);
+
+uint16_t ec_datagram_pair_process(ec_datagram_pair_t *,
+ uint16_t[EC_NUM_DEVICES]);
+
+/*****************************************************************************/
+
+#endif
--- a/master/device.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/device.c Thu Sep 06 18:28:57 2012 +0200
@@ -54,12 +54,6 @@
} while (0)
#endif
-/** List of intervals for frame statistics [s].
- */
-static const unsigned int rate_intervals[] = {
- 1, 10, 60
-};
-
/*****************************************************************************/
/** Constructor.
@@ -309,30 +303,6 @@
{
struct sk_buff *skb = device->tx_skb[device->tx_ring_index];
- // frame statistics
- if (unlikely(jiffies - device->stats_jiffies >= HZ)) {
- unsigned int i;
- u32 tx_frame_rate =
- (u32) (device->tx_count - device->last_tx_count) * 1000;
- u32 tx_byte_rate =
- (device->tx_bytes - device->last_tx_bytes);
- u64 loss = device->tx_count - device->rx_count;
- s32 loss_rate = (s32) (loss - device->last_loss) * 1000;
- for (i = 0; i < EC_RATE_COUNT; i++) {
- unsigned int n = rate_intervals[i];
- device->tx_frame_rates[i] =
- (device->tx_frame_rates[i] * (n - 1) + tx_frame_rate) / n;
- device->tx_byte_rates[i] =
- (device->tx_byte_rates[i] * (n - 1) + tx_byte_rate) / n;
- device->loss_rates[i] =
- (device->loss_rates[i] * (n - 1) + loss_rate) / n;
- }
- device->last_tx_count = device->tx_count;
- device->last_tx_bytes = device->tx_bytes;
- device->last_loss = loss;
- device->stats_jiffies = jiffies;
- }
-
// set the right length for the data
skb->len = ETH_HLEN + size;
@@ -350,7 +320,9 @@
#endif
{
device->tx_count++;
+ device->master->device_stats.tx_count++;
device->tx_bytes += ETH_HLEN + size;
+ device->master->device_stats.tx_bytes += ETH_HLEN + size;
#ifdef EC_DEBUG_IF
ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
#endif
@@ -375,16 +347,20 @@
// zero frame statistics
device->tx_count = 0;
+ device->last_tx_count = 0;
device->rx_count = 0;
+ device->last_rx_count = 0;
+ device->tx_bytes = 0;
+ device->last_tx_bytes = 0;
+ device->rx_bytes = 0;
+ device->last_rx_bytes = 0;
device->tx_errors = 0;
- device->tx_bytes = 0;
- device->last_tx_count = 0;
- device->last_tx_bytes = 0;
- device->last_loss = 0;
+
for (i = 0; i < EC_RATE_COUNT; i++) {
device->tx_frame_rates[i] = 0;
+ device->rx_frame_rates[i] = 0;
device->tx_byte_rates[i] = 0;
- device->loss_rates[i] = 0;
+ device->rx_byte_rates[i] = 0;
}
}
@@ -479,6 +455,43 @@
device->poll(device->dev);
}
+/*****************************************************************************/
+
+/** Update device statistics.
+ */
+void ec_device_update_stats(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ unsigned int i;
+
+ s32 tx_frame_rate = (device->tx_count - device->last_tx_count) * 1000;
+ s32 rx_frame_rate = (device->rx_count - device->last_rx_count) * 1000;
+ s32 tx_byte_rate = (device->tx_bytes - device->last_tx_bytes);
+ s32 rx_byte_rate = (device->rx_bytes - device->last_rx_bytes);
+
+ /* Low-pass filter:
+ * Y_n = y_(n - 1) + T / tau * (x - y_(n - 1)) | T = 1
+ * -> Y_n += (x - y_(n - 1)) / tau
+ */
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ s32 n = rate_intervals[i];
+ device->tx_frame_rates[i] +=
+ (tx_frame_rate - device->tx_frame_rates[i]) / n;
+ device->rx_frame_rates[i] +=
+ (rx_frame_rate - device->rx_frame_rates[i]) / n;
+ device->tx_byte_rates[i] +=
+ (tx_byte_rate - device->tx_byte_rates[i]) / n;
+ device->rx_byte_rates[i] +=
+ (rx_byte_rate - device->rx_byte_rates[i]) / n;
+ }
+
+ device->last_tx_count = device->tx_count;
+ device->last_rx_count = device->rx_count;
+ device->last_tx_bytes = device->tx_bytes;
+ device->last_rx_bytes = device->rx_bytes;
+}
+
/******************************************************************************
* Device interface
*****************************************************************************/
@@ -496,10 +509,21 @@
void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
{
ec_master_t *master = device->master;
- char str[20];
-
- ec_mac_print(device->dev->dev_addr, str);
- EC_MASTER_INFO(master, "Releasing main device %s.\n", str);
+ char dev_str[20], mac_str[20];
+
+ ec_mac_print(device->dev->dev_addr, mac_str);
+
+ if (device == &master->devices[EC_DEVICE_MAIN]) {
+ sprintf(dev_str, "main");
+ } else if (device == &master->devices[EC_DEVICE_BACKUP]) {
+ sprintf(dev_str, "backup");
+ } else {
+ EC_MASTER_WARN(master, "%s() called with unknown device %s!\n",
+ __func__, mac_str);
+ sprintf(dev_str, "UNKNOWN");
+ }
+
+ EC_MASTER_INFO(master, "Releasing %s device %s.\n", dev_str, mac_str);
down(&master->device_sem);
ec_device_detach(device);
@@ -516,17 +540,22 @@
int ecdev_open(ec_device_t *device /**< EtherCAT device */)
{
int ret;
+ ec_master_t *master = device->master;
ret = ec_device_open(device);
if (ret) {
- EC_MASTER_ERR(device->master, "Failed to open device!\n");
+ EC_MASTER_ERR(master, "Failed to open device!\n");
return ret;
}
- ret = ec_master_enter_idle_phase(device->master);
- if (ret) {
- EC_MASTER_ERR(device->master, "Failed to enter IDLE phase!\n");
- return ret;
+ if (master->devices[EC_DEVICE_MAIN].open &&
+ (ec_mac_is_zero(master->macs[EC_DEVICE_BACKUP]) ||
+ master->devices[EC_DEVICE_BACKUP].open)) {
+ ret = ec_master_enter_idle_phase(device->master);
+ if (ret) {
+ EC_MASTER_ERR(device->master, "Failed to enter IDLE phase!\n");
+ return ret;
+ }
}
return 0;
@@ -541,10 +570,15 @@
*/
void ecdev_close(ec_device_t *device /**< EtherCAT device */)
{
- ec_master_leave_idle_phase(device->master);
-
- if (ec_device_close(device))
- EC_MASTER_WARN(device->master, "Failed to close device!\n");
+ ec_master_t *master = device->master;
+
+ if (master->phase == EC_IDLE) {
+ ec_master_leave_idle_phase(master);
+ }
+
+ if (ec_device_close(device)) {
+ EC_MASTER_WARN(master, "Failed to close device!\n");
+ }
}
/*****************************************************************************/
@@ -572,6 +606,9 @@
}
device->rx_count++;
+ device->master->device_stats.rx_count++;
+ device->rx_bytes += size;
+ device->master->device_stats.rx_bytes += size;
if (unlikely(device->master->debug_level > 1)) {
EC_MASTER_DBG(device->master, 2, "Received frame:\n");
@@ -603,14 +640,15 @@
)
{
if (unlikely(!device)) {
- EC_MASTER_WARN(device->master, "ecdev_set_link(): No device!\n");
+ EC_WARN("ecdev_set_link() called with null device!\n");
return;
}
if (likely(state != device->link_state)) {
device->link_state = state;
EC_MASTER_INFO(device->master,
- "Link state changed to %s.\n", (state ? "UP" : "DOWN"));
+ "Link state of %s changed to %s.\n",
+ device->dev->name, (state ? "UP" : "DOWN"));
}
}
@@ -625,7 +663,7 @@
)
{
if (unlikely(!device)) {
- EC_MASTER_WARN(device->master, "ecdev_get_link(): No device!\n");
+ EC_WARN("ecdev_get_link() called with null device!\n");
return 0;
}
--- a/master/device.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/device.h Thu Sep 06 18:28:57 2012 +0200
@@ -101,19 +101,24 @@
u64 tx_count; /**< Number of frames sent. */
u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
u64 rx_count; /**< Number of frames received. */
- u64 tx_bytes; /**< Number of frames sent. */
+ u64 last_rx_count; /**< Number of frames received of last statistics
+ cycle. */
+ u64 tx_bytes; /**< Number of bytes sent. */
u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+ u64 rx_bytes; /**< Number of bytes received. */
+ u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+ */
u64 tx_errors; /**< Number of transmit errors. */
- u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */
- unsigned int tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in
- frames/s for different
- statistics cycle periods. */
- unsigned int tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s
- for different statistics
- cycle periods. */
- int loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different
- statistics cycle periods. */
- unsigned long stats_jiffies; /**< Jiffies of last statistic cycle. */
+ s32 tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s for
+ different statistics cycle periods. */
+ s32 rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s for
+ different statistics cycle periods. */
#ifdef EC_DEBUG_IF
ec_debug_t dbg; /**< debug device */
@@ -141,6 +146,7 @@
uint8_t *ec_device_tx_data(ec_device_t *);
void ec_device_send(ec_device_t *, size_t);
void ec_device_clear_stats(ec_device_t *);
+void ec_device_update_stats(ec_device_t *);
#ifdef EC_DEBUG_RING
void ec_device_debug_ring_append(ec_device_t *, ec_debug_frame_dir_t,
--- a/master/domain.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/domain.c Thu Sep 06 18:28:57 2012 +0200
@@ -41,6 +41,9 @@
#include "slave_config.h"
#include "domain.h"
+#include "datagram_pair.h"
+
+#define DEBUG_REDUNDANCY 0
/*****************************************************************************/
@@ -63,10 +66,12 @@
domain->data = NULL;
domain->data_origin = EC_ORIG_INTERNAL;
domain->logical_base_address = 0x00000000;
- INIT_LIST_HEAD(&domain->datagrams);
- domain->working_counter = 0x0000;
+ INIT_LIST_HEAD(&domain->datagram_pairs);
+ domain->working_counter[EC_DEVICE_MAIN] = 0x0000;
+ domain->working_counter[EC_DEVICE_BACKUP] = 0x0000;
domain->expected_working_counter = 0x0000;
domain->working_counter_changes = 0;
+ domain->redundancy_active = 0;
domain->notify_jiffies = 0;
}
@@ -76,12 +81,13 @@
*/
void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */)
{
- ec_datagram_t *datagram, *next;
+ ec_datagram_pair_t *datagram_pair, *next_pair;
// dequeue and free datagrams
- list_for_each_entry_safe(datagram, next, &domain->datagrams, list) {
- ec_datagram_clear(datagram);
- kfree(datagram);
+ list_for_each_entry_safe(datagram_pair, next_pair,
+ &domain->datagram_pairs, list) {
+ ec_datagram_pair_clear(datagram_pair);
+ kfree(datagram_pair);
}
ec_domain_clear_data(domain);
@@ -95,8 +101,10 @@
ec_domain_t *domain /**< EtherCAT domain. */
)
{
- if (domain->data_origin == EC_ORIG_INTERNAL && domain->data)
+ if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) {
kfree(domain->data);
+ }
+
domain->data = NULL;
domain->data_origin = EC_ORIG_INTERNAL;
}
@@ -122,68 +130,81 @@
/*****************************************************************************/
-/** Allocates a domain datagram and appends it to the list.
- *
- * The datagram type and expected working counters are determined by the
- * number of input and output fmmus that share the datagram.
+/** Allocates a domain datagram pair and appends it to the list.
+ *
+ * The datagrams' types and expected working counters are determined by the
+ * number of input and output fmmus that share the datagrams.
*
* \retval 0 Success.
* \retval <0 Error code.
*/
-int ec_domain_add_datagram(
+int ec_domain_add_datagram_pair(
ec_domain_t *domain, /**< EtherCAT domain. */
uint32_t logical_offset, /**< Logical offset. */
size_t data_size, /**< Size of the data. */
uint8_t *data, /**< Process data. */
- const unsigned int used[] /**< Used by inputs/outputs. */
- )
-{
- ec_datagram_t *datagram;
+ const unsigned int used[] /**< Slave config counter for in/out. */
+ )
+{
+ ec_datagram_pair_t *datagram_pair;
int ret;
- if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) {
+ if (!(datagram_pair = kmalloc(sizeof(ec_datagram_pair_t), GFP_KERNEL))) {
EC_MASTER_ERR(domain->master,
- "Failed to allocate domain datagram!\n");
+ "Failed to allocate domain datagram pair!\n");
return -ENOMEM;
}
- ec_datagram_init(datagram);
- snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE,
- "domain%u-%u", domain->index, logical_offset);
-
- if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs
- ret = ec_datagram_lrw(datagram, logical_offset, data_size, data);
- if (ret < 0) {
- kfree(datagram);
- return ret;
- }
- // If LRW is used, output FMMUs increment the working counter by 2,
- // while input FMMUs increment it by 1.
- domain->expected_working_counter +=
- used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT];
- } else if (used[EC_DIR_OUTPUT]) { // outputs only
- ret = ec_datagram_lwr(datagram, logical_offset, data_size, data);
- if (ret < 0) {
- kfree(datagram);
- return ret;
- }
- domain->expected_working_counter += used[EC_DIR_OUTPUT];
- } else { // inputs only (or nothing)
- ret = ec_datagram_lrd(datagram, logical_offset, data_size, data);
- if (ret < 0) {
- kfree(datagram);
- return ret;
- }
- domain->expected_working_counter += used[EC_DIR_INPUT];
- }
-
- ec_datagram_zero(datagram);
- list_add_tail(&datagram->list, &domain->datagrams);
+ ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data,
+ data_size, used);
+ if (ret) {
+ kfree(datagram_pair);
+ return ret;
+ }
+
+ domain->expected_working_counter +=
+ datagram_pair->expected_working_counter;
+
+ EC_MASTER_DBG(domain->master, 1,
+ "Adding datagram pair with expected WC %u.\n",
+ datagram_pair->expected_working_counter);
+
+
+ list_add_tail(&datagram_pair->list, &domain->datagram_pairs);
return 0;
}
/*****************************************************************************/
+/** Domain finish helper function.
+ *
+ * Detects, if a slave configuration has already been taken into account for
+ * a datagram's expected working counter calculation.
+ *
+ * Walks through the list of all FMMU configurations for the current datagram
+ * and ends before the current datagram.
+ */
+int shall_count(
+ const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to
+ search for. */
+ const ec_fmmu_config_t *first_fmmu /**< Datagram's first FMMU. */
+ )
+{
+ for (; first_fmmu != cur_fmmu;
+ first_fmmu = list_entry(first_fmmu->list.next,
+ ec_fmmu_config_t, list)) {
+
+ if (first_fmmu->sc == cur_fmmu->sc
+ && first_fmmu->dir == cur_fmmu->dir) {
+ return 0; // was already counted
+ }
+ }
+
+ return 1;
+}
+
+/*****************************************************************************/
+
/** Finishes a domain.
*
* This allocates the necessary datagrams and writes the correct logical
@@ -204,8 +225,8 @@
unsigned int datagram_count;
unsigned int datagram_used[EC_DIR_COUNT];
ec_fmmu_config_t *fmmu;
- ec_fmmu_config_t *fmmu_temp;
- const ec_datagram_t *datagram;
+ const ec_fmmu_config_t *datagram_first_fmmu = NULL;
+ const ec_datagram_pair_t *datagram_pair;
int ret;
domain->logical_base_address = base_address;
@@ -220,60 +241,57 @@
}
}
- // Cycle through all domain FMMUS and
+ // Cycle through all domain FMMUs and
// - correct the logical base addresses
// - set up the datagrams to carry the process data
+ // - calculate the datagrams' expected working counters
datagram_offset = 0;
datagram_size = 0;
datagram_count = 0;
datagram_used[EC_DIR_OUTPUT] = 0;
datagram_used[EC_DIR_INPUT] = 0;
- list_for_each_entry(fmmu_temp, &domain->fmmu_configs, list) {
- // we have to remove the constness, sorry FIXME
- ec_slave_config_t *sc = (ec_slave_config_t *) fmmu_temp->sc;
- sc->used_for_fmmu_datagram[fmmu_temp->dir] = 0;
+ if (!list_empty(&domain->fmmu_configs)) {
+ datagram_first_fmmu =
+ list_entry(domain->fmmu_configs.next, ec_fmmu_config_t, list);
}
list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
+
// Correct logical FMMU address
fmmu->logical_start_address += base_address;
// Increment Input/Output counter to determine datagram types
// and calculate expected working counters
- if (fmmu->sc->used_for_fmmu_datagram[fmmu->dir] == 0) {
- ec_slave_config_t *sc = (ec_slave_config_t *)fmmu->sc;
+ if (shall_count(fmmu, datagram_first_fmmu)) {
datagram_used[fmmu->dir]++;
- sc->used_for_fmmu_datagram[fmmu->dir] = 1;
}
// If the current FMMU's data do not fit in the current datagram,
// allocate a new one.
if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
- ret = ec_domain_add_datagram(domain,
+ ret = ec_domain_add_datagram_pair(domain,
domain->logical_base_address + datagram_offset,
datagram_size, domain->data + datagram_offset,
datagram_used);
if (ret < 0)
return ret;
+
datagram_offset += datagram_size;
datagram_size = 0;
datagram_count++;
datagram_used[EC_DIR_OUTPUT] = 0;
datagram_used[EC_DIR_INPUT] = 0;
- list_for_each_entry(fmmu_temp, &domain->fmmu_configs, list) {
- ec_slave_config_t *sc = (ec_slave_config_t *)fmmu_temp->sc;
- sc->used_for_fmmu_datagram[fmmu_temp->dir] = 0;
- }
+ datagram_first_fmmu = fmmu;
}
datagram_size += fmmu->data_size;
}
- // Allocate last datagram, if data are left (this is also the case if the
- // process data fit into a single datagram)
+ /* Allocate last datagram pair, if data are left (this is also the case if
+ * the process data fit into a single datagram) */
if (datagram_size) {
- ret = ec_domain_add_datagram(domain,
+ ret = ec_domain_add_datagram_pair(domain,
domain->logical_base_address + datagram_offset,
datagram_size, domain->data + datagram_offset,
datagram_used);
@@ -286,13 +304,16 @@
" %zu byte, expected working counter %u.\n", domain->index,
domain->logical_base_address, domain->data_size,
domain->expected_working_counter);
- list_for_each_entry(datagram, &domain->datagrams, list) {
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+ const ec_datagram_t *datagram =
+ &datagram_pair->datagrams[EC_DEVICE_MAIN];
EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x,"
" %zu byte, type %s.\n", datagram->name,
EC_READ_U32(datagram->address), datagram->data_size,
ec_datagram_type_string(datagram));
}
-
+
return 0;
}
@@ -332,8 +353,32 @@
return NULL;
}
+/*****************************************************************************/
+
+/** Process received data.
+ */
+int data_changed(
+ uint8_t *send_buffer,
+ const ec_datagram_t *datagram,
+ size_t offset,
+ size_t size
+ )
+{
+ uint8_t *sent = send_buffer + offset;
+ uint8_t *recv = datagram->data + offset;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (recv[i] != sent[i]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
/******************************************************************************
- * Realtime interface
+ * Application interface
*****************************************************************************/
int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain,
@@ -342,7 +387,7 @@
const ec_pdo_entry_reg_t *reg;
ec_slave_config_t *sc;
int ret;
-
+
EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list("
"domain = 0x%p, regs = 0x%p)\n", domain, regs);
@@ -398,20 +443,118 @@
void ecrt_domain_process(ec_domain_t *domain)
{
- uint16_t working_counter_sum;
- ec_datagram_t *datagram;
-
- working_counter_sum = 0x0000;
- list_for_each_entry(datagram, &domain->datagrams, list) {
- ec_datagram_output_stats(datagram);
- if (datagram->state == EC_DATAGRAM_RECEIVED) {
- working_counter_sum += datagram->working_counter;
- }
- }
-
- if (working_counter_sum != domain->working_counter) {
+ uint16_t wc_sum[EC_NUM_DEVICES] = {};
+ ec_datagram_pair_t *pair;
+ ec_datagram_t *main_datagram, *backup_datagram;
+ uint32_t logical_datagram_address;
+ size_t datagram_size;
+ uint16_t datagram_pair_wc;
+ unsigned int datagram_offset;
+ ec_fmmu_config_t *fmmu =
+ list_first_entry(&domain->fmmu_configs, ec_fmmu_config_t, list);
+ unsigned int redundancy;
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index);
+#endif
+
+ list_for_each_entry(pair, &domain->datagram_pairs, list) {
+
+ main_datagram = &pair->datagrams[EC_DEVICE_MAIN];
+ backup_datagram = &pair->datagrams[EC_DEVICE_BACKUP];
+ logical_datagram_address = EC_READ_U32(main_datagram->address);
+ datagram_size = main_datagram->data_size;
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n",
+ main_datagram->name, logical_datagram_address);
+#endif
+
+ datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum);
+
+ /* Go through all FMMU configs to detect data changes. */
+ list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) {
+
+ if (fmmu->dir != EC_DIR_INPUT) {
+ continue;
+ }
+
+ if (fmmu->logical_start_address >=
+ logical_datagram_address + datagram_size) {
+ // fmmu data contained in next datagram pair
+ break;
+ }
+
+ datagram_offset =
+ fmmu->logical_start_address - logical_datagram_address;
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1,
+ "input fmmu log=%u size=%u offset=%u\n",
+ fmmu->logical_start_address, fmmu->data_size,
+ datagram_offset);
+ if (domain->master->debug_level > 0) {
+ ec_print_data(pair->send_buffer + datagram_offset,
+ fmmu->data_size);
+ ec_print_data(main_datagram->data + datagram_offset,
+ fmmu->data_size);
+ ec_print_data(backup_datagram->data + datagram_offset,
+ fmmu->data_size);
+ }
+#endif
+
+ if (data_changed(pair->send_buffer, main_datagram,
+ datagram_offset, fmmu->data_size)) {
+ /* data changed on main link: no copying necessary. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "main changed\n");
+#endif
+ } else if (data_changed(pair->send_buffer, backup_datagram,
+ datagram_offset, fmmu->data_size)) {
+ /* data changed on backup link: copy to main memory. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "backup changed\n");
+#endif
+ memcpy(main_datagram->data + datagram_offset,
+ backup_datagram->data + datagram_offset,
+ fmmu->data_size);
+ } else if (datagram_pair_wc == pair->expected_working_counter) {
+ /* no change, but WC complete: use main data. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "no change but complete\n");
+#endif
+ } else {
+ /* no change and WC incomplete: mark WC as zero to avoid
+ * data.dependent WC flickering. */
+ datagram_pair_wc = 0;
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1,
+ "no change and incomplete\n");
+#endif
+ }
+ }
+ }
+
+ redundancy = wc_sum[EC_DEVICE_BACKUP] > 0;
+ if (redundancy != domain->redundancy_active) {
+ if (redundancy) {
+ EC_MASTER_WARN(domain->master,
+ "Domain %u: Redundant link in use!\n",
+ domain->index);
+ } else {
+ EC_MASTER_INFO(domain->master,
+ "Domain %u: Redundant link unused again.\n",
+ domain->index);
+ }
+ domain->redundancy_active = redundancy;
+ }
+
+ if ((wc_sum[EC_DEVICE_MAIN] != domain->working_counter[EC_DEVICE_MAIN])
+ || (wc_sum[EC_DEVICE_BACKUP]
+ != domain->working_counter[EC_DEVICE_BACKUP])) {
domain->working_counter_changes++;
- domain->working_counter = working_counter_sum;
+ domain->working_counter[EC_DEVICE_MAIN] = wc_sum[EC_DEVICE_MAIN];
+ domain->working_counter[EC_DEVICE_BACKUP] = wc_sum[EC_DEVICE_BACKUP];
}
if (domain->working_counter_changes &&
@@ -419,13 +562,19 @@
domain->notify_jiffies = jiffies;
if (domain->working_counter_changes == 1) {
EC_MASTER_INFO(domain->master, "Domain %u: Working counter"
- " changed to %u/%u.\n", domain->index,
- domain->working_counter, domain->expected_working_counter);
+ " changed to %u/%u (%u+%u).\n", domain->index,
+ domain->working_counter[EC_DEVICE_MAIN] +
+ domain->working_counter[EC_DEVICE_BACKUP],
+ domain->expected_working_counter,
+ wc_sum[EC_DEVICE_MAIN], wc_sum[EC_DEVICE_BACKUP]);
} else {
EC_MASTER_INFO(domain->master, "Domain %u: %u working counter"
- " changes - now %u/%u.\n", domain->index,
- domain->working_counter_changes, domain->working_counter,
- domain->expected_working_counter);
+ " changes - now %u/%u (%u+%u).\n", domain->index,
+ domain->working_counter_changes,
+ domain->working_counter[EC_DEVICE_MAIN] +
+ domain->working_counter[EC_DEVICE_BACKUP],
+ domain->expected_working_counter,
+ wc_sum[EC_DEVICE_MAIN], wc_sum[EC_DEVICE_BACKUP]);
}
domain->working_counter_changes = 0;
}
@@ -435,10 +584,25 @@
void ecrt_domain_queue(ec_domain_t *domain)
{
- ec_datagram_t *datagram;
-
- list_for_each_entry(datagram, &domain->datagrams, list) {
- ec_master_queue_datagram(domain->master, datagram);
+ ec_datagram_pair_t *datagram_pair;
+ ec_device_index_t dev_idx;
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+
+ /* copy main data to send buffer */
+ memcpy(datagram_pair->send_buffer,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+
+ /* copy main data to backup datagram */
+ memcpy(datagram_pair->datagrams[EC_DEVICE_BACKUP].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ ec_master_queue_datagram(domain->master,
+ &datagram_pair->datagrams[dev_idx]);
+ }
}
}
@@ -446,10 +610,12 @@
void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
{
- state->working_counter = domain->working_counter;
-
- if (domain->working_counter) {
- if (domain->working_counter == domain->expected_working_counter) {
+ state->working_counter =
+ domain->working_counter[EC_DEVICE_MAIN]
+ + domain->working_counter[EC_DEVICE_BACKUP];
+
+ if (state->working_counter) {
+ if (state->working_counter == domain->expected_working_counter) {
state->wc_state = EC_WC_COMPLETE;
} else {
state->wc_state = EC_WC_INCOMPLETE;
@@ -457,6 +623,8 @@
} else {
state->wc_state = EC_WC_ZERO;
}
+
+ state->redundancy_active = domain->redundancy_active;
}
/*****************************************************************************/
--- a/master/domain.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/domain.h Thu Sep 06 18:28:57 2012 +0200
@@ -63,12 +63,15 @@
ec_origin_t data_origin; /**< Origin of the \a data memory. */
uint32_t logical_base_address; /**< Logical offset address of the
process data. */
- struct list_head datagrams; /**< Datagrams for process data exchange. */
+ struct list_head datagram_pairs; /**< Datagrams pairs (main/backup) for
+ process data exchange. */
- uint16_t working_counter; /**< Last working counter value. */
+ uint16_t working_counter[EC_NUM_DEVICES]; /**< Last working counter
+ values. */
uint16_t expected_working_counter; /**< Expected working counter. */
unsigned int working_counter_changes; /**< Working counter changes
since last notification. */
+ unsigned int redundancy_active; /**< Non-zero, if redundancy is in use. */
unsigned long notify_jiffies; /**< Time of last notification. */
};
--- a/master/ethernet.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/ethernet.c Thu Sep 06 18:28:57 2012 +0200
@@ -384,11 +384,13 @@
*
* Starts a new receiving sequence by queueing a datagram that checks the
* slave's mailbox for a new EoE datagram.
+ *
+ * \fixme Use both devices.
*/
void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
{
if (eoe->slave->error_flag ||
- !eoe->slave->master->main_device.link_state) {
+ !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
eoe->rx_idle = 1;
eoe->tx_idle = 1;
return;
@@ -611,6 +613,8 @@
*
* Starts a new transmit sequence. If no data is available, a new receive
* sequence is started instead.
+ *
+ * \fixme Use both devices.
*/
void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
{
@@ -619,7 +623,7 @@
#endif
if (eoe->slave->error_flag ||
- !eoe->slave->master->main_device.link_state) {
+ !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
eoe->rx_idle = 1;
eoe->tx_idle = 1;
return;
--- a/master/fsm_master.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/fsm_master.c Thu Sep 06 18:28:57 2012 +0200
@@ -67,6 +67,7 @@
void ec_fsm_master_state_sdo_request(ec_fsm_master_t *);
void ec_fsm_master_state_reg_request(ec_fsm_master_t *);
+void ec_fsm_master_enter_clear_addresses(ec_fsm_master_t *);
void ec_fsm_master_enter_write_system_times(ec_fsm_master_t *);
/*****************************************************************************/
@@ -79,14 +80,19 @@
ec_datagram_t *datagram /**< Datagram object to use. */
)
{
+ ec_device_index_t dev_idx;
+
fsm->master = master;
fsm->datagram = datagram;
fsm->state = ec_fsm_master_state_start;
fsm->idle = 0;
- fsm->link_state = 0;
- fsm->slaves_responding = 0;
+ fsm->dev_idx = EC_DEVICE_MAIN;
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ fsm->link_state[dev_idx] = 0;
+ fsm->slaves_responding[dev_idx] = 0;
+ fsm->slave_states[dev_idx] = EC_SLAVE_STATE_UNKNOWN;
+ }
fsm->rescan_required = 0;
- fsm->slave_states = EC_SLAVE_STATE_UNKNOWN;
// init sub-state-machines
ec_fsm_coe_init(&fsm->fsm_coe, fsm->datagram);
@@ -159,6 +165,7 @@
ec_fsm_master_t *fsm /**< Master state machine. */
)
{
+ fsm->dev_idx = EC_DEVICE_MAIN;
fsm->state = ec_fsm_master_state_start;
fsm->state(fsm); // execute immediately
}
@@ -178,6 +185,7 @@
fsm->idle = 1;
ec_datagram_brd(fsm->datagram, 0x0130, 2);
ec_datagram_zero(fsm->datagram);
+ fsm->datagram->device_index = fsm->dev_idx;
fsm->state = ec_fsm_master_state_broadcast;
}
@@ -196,48 +204,58 @@
ec_slave_t *slave;
ec_master_t *master = fsm->master;
- if (datagram->state == EC_DATAGRAM_TIMED_OUT)
- return; // always retry
-
// bus topology change?
- if (datagram->working_counter != fsm->slaves_responding) {
+ if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
fsm->rescan_required = 1;
- fsm->slaves_responding = datagram->working_counter;
- EC_MASTER_INFO(master, "%u slave(s) responding.\n",
- fsm->slaves_responding);
- }
-
- if (fsm->link_state && !master->main_device.link_state) {
- // link went down
+ fsm->slaves_responding[fsm->dev_idx] = datagram->working_counter;
+ EC_MASTER_INFO(master, "%u slave(s) responding on %s device.\n",
+ fsm->slaves_responding[fsm->dev_idx],
+ ec_device_names[fsm->dev_idx]);
+ }
+
+ if (fsm->link_state[fsm->dev_idx] &&
+ !master->devices[fsm->dev_idx].link_state) {
+ ec_device_index_t dev_idx;
+
EC_MASTER_DBG(master, 1, "Master state machine detected "
- "link down. Clearing slave list.\n");
+ "link down on %s device. Clearing slave list.\n",
+ ec_device_names[fsm->dev_idx]);
#ifdef EC_EOE
ec_master_eoe_stop(master);
ec_master_clear_eoe_handlers(master);
#endif
ec_master_clear_slaves(master);
- fsm->slave_states = 0x00;
- fsm->slaves_responding = 0; /* reset to trigger rescan on next link
- up. */
- }
- fsm->link_state = master->main_device.link_state;
-
- if (datagram->state != EC_DATAGRAM_RECEIVED) {
- ec_fsm_master_restart(fsm);
- return;
- }
-
- if (fsm->slaves_responding) {
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ fsm->slave_states[dev_idx] = 0x00;
+ fsm->slaves_responding[dev_idx] = 0; /* Reset to trigger rescan on
+ next link up. */
+ }
+ }
+ fsm->link_state[fsm->dev_idx] = master->devices[fsm->dev_idx].link_state;
+
+ if (datagram->state == EC_DATAGRAM_RECEIVED &&
+ fsm->slaves_responding[fsm->dev_idx]) {
uint8_t states = EC_READ_U8(datagram->data);
- if (states != fsm->slave_states) { // slave states changed?
+ if (states != fsm->slave_states[fsm->dev_idx]) {
+ // slave states changed
char state_str[EC_STATE_STRING_SIZE];
- fsm->slave_states = states;
- ec_state_string(fsm->slave_states, state_str, 1);
- EC_MASTER_INFO(master, "Slave states: %s.\n", state_str);
+ fsm->slave_states[fsm->dev_idx] = states;
+ ec_state_string(states, state_str, 1);
+ EC_MASTER_INFO(master, "Slave states on %s device: %s.\n",
+ ec_device_names[fsm->dev_idx], state_str);
}
} else {
- fsm->slave_states = 0x00;
+ fsm->slave_states[fsm->dev_idx] = 0x00;
+ }
+
+ fsm->dev_idx++;
+ if (fsm->dev_idx < EC_NUM_DEVICES) {
+ // check number of responding slaves on next device
+ fsm->state = ec_fsm_master_state_start;
+ fsm->state(fsm); // execute immediately
+ return;
}
if (fsm->rescan_required) {
@@ -245,6 +263,9 @@
if (!master->allow_scan) {
up(&master->scan_sem);
} else {
+ unsigned int count = 0, next_dev_slave, ring_position;
+ ec_device_index_t dev_idx;
+
master->scan_busy = 1;
up(&master->scan_sem);
@@ -259,9 +280,12 @@
#endif
ec_master_clear_slaves(master);
- master->slave_count = fsm->slaves_responding;
-
- if (!master->slave_count) {
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES;
+ dev_idx++) {
+ count += fsm->slaves_responding[dev_idx];
+ }
+
+ if (!count) {
// no slaves present -> finish state machine.
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
@@ -269,12 +293,11 @@
return;
}
- size = sizeof(ec_slave_t) * master->slave_count;
+ size = sizeof(ec_slave_t) * count;
if (!(master->slaves =
(ec_slave_t *) kmalloc(size, GFP_KERNEL))) {
EC_MASTER_ERR(master, "Failed to allocate %u bytes"
" of slave memory!\n", size);
- master->slave_count = 0; // TODO avoid retrying scan!
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
ec_fsm_master_restart(fsm);
@@ -282,21 +305,35 @@
}
// init slaves
- for (i = 0; i < master->slave_count; i++) {
+ dev_idx = EC_DEVICE_MAIN;
+ next_dev_slave = fsm->slaves_responding[dev_idx];
+ ring_position = 0;
+ for (i = 0; i < count; i++, ring_position++) {
slave = master->slaves + i;
- ec_slave_init(slave, master, i, i + 1);
+ while (i >= next_dev_slave) {
+ dev_idx++;
+ next_dev_slave += fsm->slaves_responding[dev_idx];
+ ring_position = 0;
+ }
+
+ ec_slave_init(slave, master, dev_idx, ring_position, i + 1);
// do not force reconfiguration in operation phase to avoid
// unnecesssary process data interruptions
- if (master->phase != EC_OPERATION)
+ if (master->phase != EC_OPERATION) {
slave->force_config = 1;
+ }
}
-
- // broadcast clear all station addresses
- ec_datagram_bwr(datagram, 0x0010, 2);
- EC_WRITE_U16(datagram->data, 0x0000);
- fsm->retries = EC_FSM_RETRIES;
- fsm->state = ec_fsm_master_state_clear_addresses;
+ master->slave_count = count;
+
+ /* start with first device with slaves responding; at least one
+ * has responding slaves, otherwise count would be zero. */
+ fsm->dev_idx = EC_DEVICE_MAIN;
+ while (!fsm->slaves_responding[fsm->dev_idx]) {
+ fsm->dev_idx++;
+ }
+
+ ec_fsm_master_enter_clear_addresses(fsm);
return;
}
}
@@ -318,6 +355,7 @@
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
0x0130, 2);
ec_datagram_zero(datagram);
+ fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_read_state;
}
@@ -411,6 +449,7 @@
request->offset, request->length);
memcpy(fsm->datagram->data, request->data, request->length);
}
+ fsm->datagram->device_index = request->slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_reg_request;
return 1;
@@ -485,8 +524,9 @@
ec_slave_t *slave;
// Check for pending internal SDO requests
- if (ec_fsm_master_action_process_sdo(fsm))
- return;
+ if (ec_fsm_master_action_process_sdo(fsm)) {
+ return;
+ }
// enable processing of requests
for (slave = master->slaves;
@@ -518,6 +558,7 @@
fsm->state = ec_fsm_master_state_sdo_dictionary;
ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
return;
}
@@ -550,6 +591,7 @@
ec_datagram_fprd(fsm->datagram,
fsm->slave->station_address, 0x0130, 2);
ec_datagram_zero(fsm->datagram);
+ fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_read_state;
return;
@@ -607,6 +649,7 @@
fsm->state = ec_fsm_master_state_configure_slave;
ec_fsm_slave_config_start(&fsm->fsm_slave_config, slave);
fsm->state(fsm); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
return;
}
@@ -680,8 +723,9 @@
{
ec_slave_t *slave = fsm->slave;
- if (ec_fsm_change_exec(&fsm->fsm_change))
- return;
+ if (ec_fsm_change_exec(&fsm->fsm_change)) {
+ return;
+ }
if (!ec_fsm_change_success(&fsm->fsm_change)) {
fsm->slave->error_flag = 1;
@@ -693,6 +737,22 @@
/*****************************************************************************/
+/** Start clearing slave addresses.
+ */
+void ec_fsm_master_enter_clear_addresses(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ // broadcast clear all station addresses
+ ec_datagram_bwr(fsm->datagram, 0x0010, 2);
+ EC_WRITE_U16(fsm->datagram->data, 0x0000);
+ fsm->datagram->device_index = fsm->dev_idx;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_clear_addresses;
+}
+
+/*****************************************************************************/
+
/** Master state: CLEAR ADDRESSES.
*/
void ec_fsm_master_state_clear_addresses(
@@ -702,12 +762,14 @@
ec_master_t *master = fsm->master;
ec_datagram_t *datagram = fsm->datagram;
- if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
- return;
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
if (datagram->state != EC_DATAGRAM_RECEIVED) {
EC_MASTER_ERR(master, "Failed to receive address"
- " clearing datagram: ");
+ " clearing datagram on %s link: ",
+ ec_device_names[fsm->dev_idx]);
ec_datagram_print_state(datagram);
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
@@ -715,17 +777,20 @@
return;
}
- if (datagram->working_counter != master->slave_count) {
- EC_MASTER_WARN(master, "Failed to clear all station addresses:"
+ if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
+ EC_MASTER_WARN(master, "Failed to clear station addresses on %s link:"
" Cleared %u of %u",
- datagram->working_counter, master->slave_count);
+ ec_device_names[fsm->dev_idx], datagram->working_counter,
+ fsm->slaves_responding[fsm->dev_idx]);
}
EC_MASTER_DBG(master, 1, "Sending broadcast-write"
- " to measure transmission delays.\n");
+ " to measure transmission delays on %s link.\n",
+ ec_device_names[fsm->dev_idx]);
ec_datagram_bwr(datagram, 0x0900, 1);
ec_datagram_zero(datagram);
+ fsm->datagram->device_index = fsm->dev_idx;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_measure_delays;
}
@@ -745,7 +810,8 @@
return;
if (datagram->state != EC_DATAGRAM_RECEIVED) {
- EC_MASTER_ERR(master, "Failed to receive delay measuring datagram: ");
+ EC_MASTER_ERR(master, "Failed to receive delay measuring datagram"
+ " on %s link: ", ec_device_names[fsm->dev_idx]);
ec_datagram_print_state(datagram);
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
@@ -753,16 +819,30 @@
return;
}
- EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring.\n",
- datagram->working_counter);
+ EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring"
+ " on %s link.\n",
+ datagram->working_counter, ec_device_names[fsm->dev_idx]);
+
+ do {
+ fsm->dev_idx++;
+ } while (fsm->dev_idx < EC_NUM_DEVICES &&
+ !fsm->slaves_responding[fsm->dev_idx]);
+ if (fsm->dev_idx < EC_NUM_DEVICES) {
+ ec_fsm_master_enter_clear_addresses(fsm);
+ return;
+ }
EC_MASTER_INFO(master, "Scanning bus.\n");
// begin scanning of slaves
fsm->slave = master->slaves;
+ EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+ fsm->slave->ring_position,
+ ec_device_names[fsm->slave->device_index]);
fsm->state = ec_fsm_master_state_scan_slave;
ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
}
/*****************************************************************************/
@@ -779,8 +859,10 @@
#ifdef EC_EOE
ec_slave_t *slave = fsm->slave;
#endif
- if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan))
- return;
+
+ if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan)) {
+ return;
+ }
#ifdef EC_EOE
if (slave->sii.mailbox_protocols & EC_MBOX_EOE) {
@@ -800,8 +882,12 @@
// another slave to fetch?
fsm->slave++;
if (fsm->slave < master->slaves + master->slave_count) {
+ EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+ fsm->slave->ring_position,
+ ec_device_names[fsm->slave->device_index]);
ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
return;
}
@@ -843,8 +929,9 @@
{
ec_master_t *master = fsm->master;
- if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config))
- return;
+ if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config)) {
+ return;
+ }
fsm->slave->force_config = 0;
@@ -886,6 +973,7 @@
// and time offset (0x0920, 64 bit)
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
0x0910, 24);
+ fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_read_offset;
return;
@@ -1031,6 +1119,7 @@
ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12);
EC_WRITE_U64(datagram->data, new_offset);
EC_WRITE_U32(datagram->data + 8, slave->transmission_delay);
+ fsm->datagram->device_index = slave->device_index;
fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_write_offset;
}
@@ -1135,7 +1224,9 @@
ec_slave_t *slave = fsm->slave;
ec_master_t *master = fsm->master;
- if (ec_fsm_coe_exec(&fsm->fsm_coe)) return;
+ if (ec_fsm_coe_exec(&fsm->fsm_coe)) {
+ return;
+ }
if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
ec_fsm_master_restart(fsm);
--- a/master/fsm_master.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/fsm_master.h Thu Sep 06 18:28:57 2012 +0200
@@ -115,12 +115,19 @@
unsigned int retries; /**< retries on datagram timeout. */
void (*state)(ec_fsm_master_t *); /**< master state function */
+ ec_device_index_t dev_idx; /**< Current device index (for scanning etc.).
+ */
int idle; /**< state machine is in idle phase */
unsigned long scan_jiffies; /**< beginning of slave scanning */
- uint8_t link_state; /**< Last main device link state. */
- unsigned int slaves_responding; /**< number of responding slaves */
+ uint8_t link_state[EC_NUM_DEVICES]; /**< Last link state for every device.
+ */
+ unsigned int slaves_responding[EC_NUM_DEVICES]; /**< Number of responding
+ slaves for every device.
+ */
unsigned int rescan_required; /**< A bus rescan is required. */
- ec_slave_state_t slave_states; /**< states of responding slaves */
+ ec_slave_state_t slave_states[EC_NUM_DEVICES]; /**< AL states of
+ responding slaves for
+ every device. */
ec_slave_t *slave; /**< current slave */
ec_sii_write_request_t *sii_request; /**< SII write request */
off_t sii_index; /**< index to SII write request data */
--- a/master/fsm_slave.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/fsm_slave.c Thu Sep 06 18:28:57 2012 +0200
@@ -145,16 +145,19 @@
)
{
// Check for pending external SDO requests
- if (ec_fsm_slave_action_process_sdo(fsm))
- return;
+ if (ec_fsm_slave_action_process_sdo(fsm)) {
+ return;
+ }
// Check for pending FoE requests
- if (ec_fsm_slave_action_process_foe(fsm))
- return;
+ if (ec_fsm_slave_action_process_foe(fsm)) {
+ return;
+ }
// Check for pending SoE requests
- if (ec_fsm_slave_action_process_soe(fsm))
- return;
+ if (ec_fsm_slave_action_process_soe(fsm)) {
+ return;
+ }
}
/*****************************************************************************/
--- a/master/globals.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/globals.h Thu Sep 06 18:28:57 2012 +0200
@@ -196,6 +196,16 @@
EC_SDO_ENTRY_ACCESS_COUNT /**< Number of states. */
};
+/** Master devices.
+ */
+typedef enum {
+ EC_DEVICE_MAIN, /**< Main device. */
+ EC_DEVICE_BACKUP, /**< Backup device */
+ EC_NUM_DEVICES /**< Number of devices. */
+} ec_device_index_t;
+
+extern const char *ec_device_names[EC_NUM_DEVICES];
+
/*****************************************************************************/
/** Convenience macro for printing EtherCAT-specific information to syslog.
--- a/master/ioctl.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/ioctl.h Thu Sep 06 18:28:57 2012 +0200
@@ -56,7 +56,7 @@
*
* Increment this when changing the ioctl interface!
*/
-#define EC_IOCTL_VERSION_MAGIC 12
+#define EC_IOCTL_VERSION_MAGIC 18
// Command-line tool
#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)
@@ -101,42 +101,43 @@
#define EC_IOCTL_SEND EC_IO(0x23)
#define EC_IOCTL_RECEIVE EC_IO(0x24)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x25, ec_master_state_t)
-#define EC_IOCTL_APP_TIME EC_IOW(0x26, ec_ioctl_app_time_t)
-#define EC_IOCTL_SYNC_REF EC_IO(0x27)
-#define EC_IOCTL_SYNC_SLAVES EC_IO(0x28)
-#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x29)
-#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2a, uint32_t)
-#define EC_IOCTL_RESET EC_IO(0x2b)
-#define EC_IOCTL_SC_SYNC EC_IOW(0x2c, ec_ioctl_config_t)
-#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x2d, ec_ioctl_config_t)
-#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x2e, ec_ioctl_config_pdo_t)
-#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x2f, ec_ioctl_config_pdo_t)
-#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x30, ec_ioctl_add_pdo_entry_t)
-#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x31, ec_ioctl_config_pdo_t)
-#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x32, ec_ioctl_reg_pdo_entry_t)
-#define EC_IOCTL_SC_DC EC_IOW(0x33, ec_ioctl_config_t)
-#define EC_IOCTL_SC_SDO EC_IOW(0x34, ec_ioctl_sc_sdo_t)
-#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x35, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SC_VOE EC_IOWR(0x36, ec_ioctl_voe_t)
-#define EC_IOCTL_SC_STATE EC_IOWR(0x37, ec_ioctl_sc_state_t)
-#define EC_IOCTL_SC_IDN EC_IOW(0x38, ec_ioctl_sc_idn_t)
-#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x39)
-#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x3a)
-#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x3b)
-#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x3c, ec_ioctl_domain_state_t)
-#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x3d, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x3e, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x3f, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x40, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x41, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x42, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x43, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ EC_IOW(0x44, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x45, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_WRITE EC_IOWR(0x46, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_EXEC EC_IOWR(0x47, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_DATA EC_IOWR(0x48, ec_ioctl_voe_t)
-#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x49, size_t)
+#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x26, ec_ioctl_link_state_t)
+#define EC_IOCTL_APP_TIME EC_IOW(0x27, ec_ioctl_app_time_t)
+#define EC_IOCTL_SYNC_REF EC_IO(0x28)
+#define EC_IOCTL_SYNC_SLAVES EC_IO(0x29)
+#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2a)
+#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2b, uint32_t)
+#define EC_IOCTL_RESET EC_IO(0x2c)
+#define EC_IOCTL_SC_SYNC EC_IOW(0x2d, ec_ioctl_config_t)
+#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x2e, ec_ioctl_config_t)
+#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x2f, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x20, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x31, ec_ioctl_add_pdo_entry_t)
+#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x32, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x33, ec_ioctl_reg_pdo_entry_t)
+#define EC_IOCTL_SC_DC EC_IOW(0x34, ec_ioctl_config_t)
+#define EC_IOCTL_SC_SDO EC_IOW(0x35, ec_ioctl_sc_sdo_t)
+#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x36, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SC_VOE EC_IOWR(0x37, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_STATE EC_IOWR(0x38, ec_ioctl_sc_state_t)
+#define EC_IOCTL_SC_IDN EC_IOW(0x39, ec_ioctl_sc_idn_t)
+#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x3a)
+#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x3b)
+#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x3c)
+#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x3d, ec_ioctl_domain_state_t)
+#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x3e, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x3f, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x30, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x41, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x42, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x43, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x44, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ EC_IOW(0x45, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x46, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_WRITE EC_IOWR(0x47, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_EXEC EC_IOWR(0x48, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_DATA EC_IOWR(0x49, ec_ioctl_voe_t)
+#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x4a, size_t)
/*****************************************************************************/
@@ -168,11 +169,22 @@
uint64_t tx_count;
uint64_t rx_count;
uint64_t tx_bytes;
+ uint64_t rx_bytes;
uint64_t tx_errors;
- uint32_t tx_frame_rates[EC_RATE_COUNT];
- uint32_t tx_byte_rates[EC_RATE_COUNT];
- int32_t loss_rates[EC_RATE_COUNT];
- } devices[2];
+ int32_t tx_frame_rates[EC_RATE_COUNT];
+ int32_t rx_frame_rates[EC_RATE_COUNT];
+ int32_t tx_byte_rates[EC_RATE_COUNT];
+ int32_t rx_byte_rates[EC_RATE_COUNT];
+ } devices[EC_NUM_DEVICES];
+ uint64_t tx_count;
+ uint64_t rx_count;
+ uint64_t tx_bytes;
+ uint64_t rx_bytes;
+ int32_t tx_frame_rates[EC_RATE_COUNT];
+ int32_t rx_frame_rates[EC_RATE_COUNT];
+ int32_t tx_byte_rates[EC_RATE_COUNT];
+ int32_t rx_byte_rates[EC_RATE_COUNT];
+ int32_t loss_rates[EC_RATE_COUNT];
uint64_t app_time;
uint16_t ref_clock;
} ec_ioctl_master_t;
@@ -184,6 +196,7 @@
uint16_t position;
// outputs
+ unsigned int device_index;
uint32_t vendor_id;
uint32_t product_code;
uint32_t revision_number;
@@ -279,7 +292,7 @@
// outputs
uint32_t data_size;
uint32_t logical_base_address;
- uint16_t working_counter;
+ uint16_t working_counter[EC_NUM_DEVICES];
uint16_t expected_working_counter;
uint32_t fmmu_count;
} ec_ioctl_domain_t;
@@ -661,6 +674,16 @@
typedef struct {
// inputs
+ uint32_t dev_idx;
+
+ // outputs
+ ec_master_link_state_t *state;
+} ec_ioctl_link_state_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
uint64_t app_time;
} ec_ioctl_app_time_t;
--- a/master/master.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/master.c Thu Sep 06 18:28:57 2012 +0200
@@ -82,6 +82,12 @@
#endif
+/** List of intervals for statistics [s].
+ */
+const unsigned int rate_intervals[] = {
+ 1, 10, 60
+};
+
/*****************************************************************************/
void ec_master_clear_slave_configs(ec_master_t *);
@@ -92,6 +98,8 @@
static int ec_master_eoe_thread(void *);
#endif
void ec_master_find_dc_ref_clock(ec_master_t *);
+void ec_master_clear_device_stats(ec_master_t *);
+void ec_master_update_device_stats(ec_master_t *);
/*****************************************************************************/
@@ -101,11 +109,13 @@
{
#ifdef EC_HAVE_CYCLES
timeout_cycles = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000);
- ext_injection_timeout_cycles = (cycles_t) EC_SDO_INJECTION_TIMEOUT /* us */ * (cpu_khz / 1000);
+ ext_injection_timeout_cycles =
+ (cycles_t) EC_SDO_INJECTION_TIMEOUT /* us */ * (cpu_khz / 1000);
#else
// one jiffy may always elapse between time measurement
timeout_jiffies = max(EC_IO_TIMEOUT * HZ / 1000000, 1);
- ext_injection_timeout_jiffies = max(EC_SDO_INJECTION_TIMEOUT * HZ / 1000000, 1);
+ ext_injection_timeout_jiffies =
+ max(EC_SDO_INJECTION_TIMEOUT * HZ / 1000000, 1);
#endif
}
@@ -132,8 +142,9 @@
sema_init(&master->master_sem, 1);
- master->main_mac = main_mac;
- master->backup_mac = backup_mac;
+ master->macs[EC_DEVICE_MAIN] = main_mac;
+ master->macs[EC_DEVICE_BACKUP] = backup_mac;
+ ec_master_clear_device_stats(master);
sema_init(&master->device_sem, 1);
@@ -201,11 +212,11 @@
init_waitqueue_head(&master->reg_queue);
// init devices
- ret = ec_device_init(&master->main_device, master);
+ ret = ec_device_init(&master->devices[EC_DEVICE_MAIN], master);
if (ret < 0)
goto out_return;
- ret = ec_device_init(&master->backup_device, master);
+ ret = ec_device_init(&master->devices[EC_DEVICE_BACKUP], master);
if (ret < 0)
goto out_clear_main;
@@ -224,7 +235,8 @@
// init reference sync datagram
ec_datagram_init(&master->ref_sync_datagram);
- snprintf(master->ref_sync_datagram.name, EC_DATAGRAM_NAME_SIZE, "refsync");
+ snprintf(master->ref_sync_datagram.name, EC_DATAGRAM_NAME_SIZE,
+ "refsync");
ret = ec_datagram_apwr(&master->ref_sync_datagram, 0, 0x0910, 8);
if (ret < 0) {
ec_datagram_clear(&master->ref_sync_datagram);
@@ -246,7 +258,8 @@
// init sync monitor datagram
ec_datagram_init(&master->sync_mon_datagram);
- snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE, "syncmon");
+ snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE,
+ "syncmon");
ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
if (ret < 0) {
ec_datagram_clear(&master->sync_mon_datagram);
@@ -299,9 +312,9 @@
ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram);
out_clear_backup:
- ec_device_clear(&master->backup_device);
+ ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
out_clear_main:
- ec_device_clear(&master->main_device);
+ ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
out_return:
return ret;
}
@@ -334,8 +347,8 @@
ec_datagram_clear(&master->ref_sync_datagram);
ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram);
- ec_device_clear(&master->backup_device);
- ec_device_clear(&master->main_device);
+ ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
+ ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
}
/*****************************************************************************/
@@ -541,6 +554,7 @@
)
{
int ret;
+ ec_device_index_t dev_idx;
EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n");
@@ -551,7 +565,9 @@
master->phase = EC_IDLE;
// reset number of responding slaves to trigger scanning
- master->fsm.slaves_responding = 0;
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ master->fsm.slaves_responding[dev_idx] = 0;
+ }
ret = ec_master_thread_start(master, ec_master_idle_thread,
"EtherCAT-IDLE");
@@ -585,7 +601,9 @@
/** Transition function from IDLE to OPERATION phase.
*/
-int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
+int ec_master_enter_operation_phase(
+ ec_master_t *master /**< EtherCAT master */
+ )
{
int ret = 0;
ec_slave_t *slave;
@@ -622,7 +640,8 @@
up(&master->scan_sem);
// wait for slave scan to complete
- ret = wait_event_interruptible(master->scan_queue, !master->scan_busy);
+ ret = wait_event_interruptible(master->scan_queue,
+ !master->scan_busy);
if (ret) {
EC_MASTER_INFO(master, "Waiting for slave scan"
" interrupted by signal.\n");
@@ -712,8 +731,7 @@
#endif
datagram->jiffies_sent = 0;
ec_master_queue_datagram(master, datagram);
- }
- else {
+ } else {
if (datagram->data_size > master->max_queue_size) {
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR;
@@ -880,14 +898,17 @@
/*****************************************************************************/
-/** Sends the datagrams in the queue.
+/** Sends the datagrams in the queue for a certain device.
*
*/
-void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
+void ec_master_send_datagrams(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_device_index_t device_index /**< Device index. */
+ )
{
ec_datagram_t *datagram, *next;
size_t datagram_size;
- uint8_t *frame_data, *cur_data;
+ uint8_t *frame_data, *cur_data = NULL;
void *follows_word;
#ifdef EC_HAVE_CYCLES
cycles_t cycles_start, cycles_sent, cycles_end;
@@ -902,18 +923,27 @@
frame_count = 0;
INIT_LIST_HEAD(&sent_datagrams);
- EC_MASTER_DBG(master, 2, "ec_master_send_datagrams\n");
+ EC_MASTER_DBG(master, 2, "%s(device_index = %u)\n",
+ __func__, device_index);
do {
- // fetch pointer to transmit socket buffer
- frame_data = ec_device_tx_data(&master->main_device);
- cur_data = frame_data + EC_FRAME_HEADER_SIZE;
+ frame_data = NULL;
follows_word = NULL;
more_datagrams_waiting = 0;
// fill current frame with datagrams
list_for_each_entry(datagram, &master->datagram_queue, queue) {
- if (datagram->state != EC_DATAGRAM_QUEUED) continue;
+ if (datagram->state != EC_DATAGRAM_QUEUED ||
+ datagram->device_index != device_index) {
+ continue;
+ }
+
+ if (!frame_data) {
+ // fetch pointer to transmit socket buffer
+ frame_data =
+ ec_device_tx_data(&master->devices[device_index]);
+ cur_data = frame_data + EC_FRAME_HEADER_SIZE;
+ }
// does the current datagram fit in the frame?
datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size
@@ -926,15 +956,17 @@
list_add_tail(&datagram->sent, &sent_datagrams);
datagram->index = master->datagram_index++;
- EC_MASTER_DBG(master, 2, "adding datagram 0x%02X\n",
+ EC_MASTER_DBG(master, 2, "Adding datagram 0x%02X\n",
datagram->index);
- // set "datagram following" flag in previous frame
- if (follows_word)
- EC_WRITE_U16(follows_word, EC_READ_U16(follows_word) | 0x8000);
+ // set "datagram following" flag in previous datagram
+ if (follows_word) {
+ EC_WRITE_U16(follows_word,
+ EC_READ_U16(follows_word) | 0x8000);
+ }
// EtherCAT datagram header
- EC_WRITE_U8 (cur_data, datagram->type);
+ EC_WRITE_U8 (cur_data, datagram->type);
EC_WRITE_U8 (cur_data + 1, datagram->index);
memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN);
EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF);
@@ -958,7 +990,7 @@
// EtherCAT frame header
EC_WRITE_U16(frame_data, ((cur_data - frame_data
- - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
+ - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
// pad frame
while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN)
@@ -967,7 +999,8 @@
EC_MASTER_DBG(master, 2, "frame size: %zu\n", cur_data - frame_data);
// send frame
- ec_device_send(&master->main_device, cur_data - frame_data);
+ ec_device_send(&master->devices[device_index],
+ cur_data - frame_data);
#ifdef EC_HAVE_CYCLES
cycles_sent = get_cycles();
#endif
@@ -990,8 +1023,8 @@
#ifdef EC_HAVE_CYCLES
if (unlikely(master->debug_level > 1)) {
cycles_end = get_cycles();
- EC_MASTER_DBG(master, 0, "ec_master_send_datagrams"
- " sent %u frames in %uus.\n", frame_count,
+ EC_MASTER_DBG(master, 0, "%s()"
+ " sent %u frames in %uus.\n", __func__, frame_count,
(unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
}
#endif
@@ -1090,7 +1123,7 @@
EC_DATAGRAM_HEADER_SIZE + data_size
+ EC_DATAGRAM_FOOTER_SIZE);
#ifdef EC_DEBUG_RING
- ec_device_debug_ring_print(&master->main_device);
+ ec_device_debug_ring_print(&master->devices[EC_DEVICE_MAIN]);
#endif
}
@@ -1115,9 +1148,11 @@
// dequeue the received datagram
datagram->state = EC_DATAGRAM_RECEIVED;
#ifdef EC_HAVE_CYCLES
- datagram->cycles_received = master->main_device.cycles_poll;
-#endif
- datagram->jiffies_received = master->main_device.jiffies_poll;
+ datagram->cycles_received =
+ master->devices[EC_DEVICE_MAIN].cycles_poll;
+#endif
+ datagram->jiffies_received =
+ master->devices[EC_DEVICE_MAIN].jiffies_poll;
list_del_init(&datagram->queue);
}
}
@@ -1155,6 +1190,83 @@
}
}
+/*****************************************************************************/
+
+/** Clears the common device statistics.
+ */
+void ec_master_clear_device_stats(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ unsigned int i;
+
+ // zero frame statistics
+ master->device_stats.tx_count = 0;
+ master->device_stats.last_tx_count = 0;
+ master->device_stats.rx_count = 0;
+ master->device_stats.last_rx_count = 0;
+ master->device_stats.tx_bytes = 0;
+ master->device_stats.last_tx_bytes = 0;
+ master->device_stats.rx_bytes = 0;
+ master->device_stats.last_rx_bytes = 0;
+ master->device_stats.last_loss = 0;
+
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ master->device_stats.tx_frame_rates[i] = 0;
+ master->device_stats.tx_byte_rates[i] = 0;
+ master->device_stats.loss_rates[i] = 0;
+ }
+}
+
+/*****************************************************************************/
+
+/** Updates the common device statistics.
+ */
+void ec_master_update_device_stats(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ ec_device_stats_t *s = &master->device_stats;
+ s32 tx_frame_rate, rx_frame_rate, tx_byte_rate, rx_byte_rate, loss_rate;
+ u64 loss;
+ unsigned int i;
+
+ // frame statistics
+ if (likely(jiffies - s->jiffies < HZ)) {
+ return;
+ }
+
+ tx_frame_rate = (s->tx_count - s->last_tx_count) * 1000;
+ rx_frame_rate = (s->rx_count - s->last_rx_count) * 1000;
+ tx_byte_rate = s->tx_bytes - s->last_tx_bytes;
+ rx_byte_rate = s->rx_bytes - s->last_rx_bytes;
+ loss = s->tx_count - s->rx_count;
+ loss_rate = (loss - s->last_loss) * 1000;
+
+ /* Low-pass filter:
+ * Y_n = y_(n - 1) + T / tau * (x - y_(n - 1)) | T = 1
+ * -> Y_n += (x - y_(n - 1)) / tau
+ */
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ s32 n = rate_intervals[i];
+ s->tx_frame_rates[i] += (tx_frame_rate - s->tx_frame_rates[i]) / n;
+ s->rx_frame_rates[i] += (rx_frame_rate - s->rx_frame_rates[i]) / n;
+ s->tx_byte_rates[i] += (tx_byte_rate - s->tx_byte_rates[i]) / n;
+ s->rx_byte_rates[i] += (rx_byte_rate - s->rx_byte_rates[i]) / n;
+ s->loss_rates[i] += (loss_rate - s->loss_rates[i]) / n;
+ }
+
+ s->last_tx_count = s->tx_count;
+ s->last_rx_count = s->rx_count;
+ s->last_tx_bytes = s->tx_bytes;
+ s->last_rx_bytes = s->rx_bytes;
+ s->last_loss = loss;
+
+ ec_device_update_stats(&master->devices[EC_DEVICE_MAIN]);
+ ec_device_update_stats(&master->devices[EC_DEVICE_BACKUP]);
+
+ s->jiffies = jiffies;
+}
/*****************************************************************************/
@@ -1278,8 +1390,8 @@
}
ecrt_master_send(master);
#ifdef EC_USE_HRTIMER
- sent_bytes = master->main_device.tx_skb[
- master->main_device.tx_ring_index]->len;
+ sent_bytes = master->devices[EC_DEVICE_MAIN].tx_skb[
+ master->devices[EC_DEVICE_MAIN].tx_ring_index]->len;
#endif
up(&master->io_sem);
@@ -1932,7 +2044,8 @@
EC_MASTER_DBG(master, 1, "ecrt_master_create_domain(master = 0x%p)\n",
master);
- if (!(domain = (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
+ if (!(domain =
+ (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
EC_MASTER_ERR(master, "Error allocating domain memory!\n");
return ERR_PTR(-ENOMEM);
}
@@ -2115,32 +2228,42 @@
void ecrt_master_send(ec_master_t *master)
{
ec_datagram_t *datagram, *n;
+ ec_device_index_t dev_idx;
if (master->injection_seq_rt != master->injection_seq_fsm) {
- // inject datagrams produced by master & slave FSMs
+ // inject datagrams produced by master and slave FSMs
ec_master_queue_datagram(master, &master->fsm_datagram);
master->injection_seq_rt = master->injection_seq_fsm;
}
ec_master_inject_external_datagrams(master);
- if (unlikely(!master->main_device.link_state)) {
- // link is down, no datagram can be sent
- list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) {
- datagram->state = EC_DATAGRAM_ERROR;
- list_del_init(&datagram->queue);
- }
-
- // query link state
- ec_device_poll(&master->main_device);
-
- // clear frame statistics
- ec_device_clear_stats(&master->main_device);
- return;
- }
-
- // send frames
- ec_master_send_datagrams(master);
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ if (unlikely(!master->devices[dev_idx].link_state)) {
+ // link is down, no datagram can be sent
+ list_for_each_entry_safe(datagram, n,
+ &master->datagram_queue, queue) {
+ if (datagram->device_index == dev_idx) {
+ datagram->state = EC_DATAGRAM_ERROR;
+ list_del_init(&datagram->queue);
+ }
+ }
+
+ if (!master->devices[dev_idx].dev) {
+ continue;
+ }
+
+ // query link state
+ ec_device_poll(&master->devices[dev_idx]);
+
+ // clear frame statistics
+ ec_device_clear_stats(&master->devices[dev_idx]);
+ continue;
+ }
+
+ // send frames
+ ec_master_send_datagrams(master, dev_idx);
+ }
}
/*****************************************************************************/
@@ -2150,18 +2273,22 @@
ec_datagram_t *datagram, *next;
// receive datagrams
- ec_device_poll(&master->main_device);
+ ec_device_poll(&master->devices[EC_DEVICE_MAIN]);
+ if (master->devices[EC_DEVICE_BACKUP].dev) {
+ ec_device_poll(&master->devices[EC_DEVICE_BACKUP]);
+ }
+ ec_master_update_device_stats(master);
// dequeue all datagrams that timed out
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
if (datagram->state != EC_DATAGRAM_SENT) continue;
#ifdef EC_HAVE_CYCLES
- if (master->main_device.cycles_poll - datagram->cycles_sent
- > timeout_cycles) {
+ if (master->devices[EC_DEVICE_MAIN].cycles_poll -
+ datagram->cycles_sent > timeout_cycles) {
#else
- if (master->main_device.jiffies_poll - datagram->jiffies_sent
- > timeout_jiffies) {
+ if (master->devices[EC_DEVICE_MAIN].jiffies_poll -
+ datagram->jiffies_sent > timeout_jiffies) {
#endif
list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_TIMED_OUT;
@@ -2171,10 +2298,12 @@
if (unlikely(master->debug_level > 0)) {
unsigned int time_us;
#ifdef EC_HAVE_CYCLES
- time_us = (unsigned int) (master->main_device.cycles_poll -
+ time_us = (unsigned int)
+ (master->devices[EC_DEVICE_MAIN].cycles_poll -
datagram->cycles_sent) * 1000 / cpu_khz;
#else
- time_us = (unsigned int) ((master->main_device.jiffies_poll -
+ time_us = (unsigned int)
+ ((master->devices[EC_DEVICE_MAIN].jiffies_poll -
datagram->jiffies_sent) * 1000000 / HZ);
#endif
EC_MASTER_DBG(master, 0, "TIMED OUT datagram %p,"
@@ -2279,7 +2408,7 @@
" master_info = 0x%p)\n", master, master_info);
master_info->slave_count = master->slave_count;
- master_info->link_up = master->main_device.link_state;
+ master_info->link_up = master->devices[EC_DEVICE_MAIN].link_state;
master_info->scan_busy = master->scan_busy;
master_info->app_time = master->app_time;
return 0;
@@ -2358,9 +2487,38 @@
void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
{
- state->slaves_responding = master->fsm.slaves_responding;
- state->al_states = master->fsm.slave_states;
- state->link_up = master->main_device.link_state;
+ ec_device_index_t dev_idx;
+
+ state->slaves_responding = 0U;
+ state->al_states = 0;
+ state->link_up = 0U;
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ /* Announce sum of responding slaves on all links. */
+ state->slaves_responding += master->fsm.slaves_responding[dev_idx];
+
+ /* Binary-or slave states of all links. */
+ state->al_states |= master->fsm.slave_states[dev_idx];
+
+ /* Signal link up if at least one device has link. */
+ state->link_up |= master->devices[dev_idx].link_state;
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_master_link_state(const ec_master_t *master, unsigned int dev_idx,
+ ec_master_link_state_t *state)
+{
+ if (dev_idx >= EC_NUM_DEVICES) {
+ return -EINVAL;
+ }
+
+ state->slaves_responding = master->fsm.slaves_responding[dev_idx];
+ state->al_states = master->fsm.slave_states[dev_idx];
+ state->link_up = master->devices[dev_idx].link_state;
+
+ return 0;
}
/*****************************************************************************/
@@ -2838,6 +2996,7 @@
EXPORT_SYMBOL(ecrt_master_get_slave);
EXPORT_SYMBOL(ecrt_master_slave_config);
EXPORT_SYMBOL(ecrt_master_state);
+EXPORT_SYMBOL(ecrt_master_link_state);
EXPORT_SYMBOL(ecrt_master_application_time);
EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
--- a/master/master.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/master.h Thu Sep 06 18:28:57 2012 +0200
@@ -137,6 +137,37 @@
/*****************************************************************************/
+/** Device statistics.
+ */
+typedef struct {
+ u64 tx_count; /**< Number of frames sent. */
+ u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
+ u64 rx_count; /**< Number of frames received. */
+ u64 last_rx_count; /**< Number of frames received of last statistics
+ cycle. */
+ u64 tx_bytes; /**< Number of bytes sent. */
+ u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+ u64 rx_bytes; /**< Number of bytes received. */
+ u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+ */
+ u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */
+ s32 tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s for
+ different statistics cycle periods. */
+ s32 rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s for
+ different statistics cycle periods. */
+ s32 loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different
+ statistics cycle periods. */
+ unsigned long jiffies; /**< Jiffies of last statistic cycle. */
+} ec_device_stats_t;
+
+/*****************************************************************************/
+
/** EtherCAT master.
*
* Manages slaves, domains and IO.
@@ -154,11 +185,10 @@
struct semaphore master_sem; /**< Master semaphore. */
- ec_device_t main_device; /**< EtherCAT main device. */
- const uint8_t *main_mac; /**< MAC address of main device. */
- ec_device_t backup_device; /**< EtherCAT backup device. */
- const uint8_t *backup_mac; /**< MAC address of backup device. */
+ ec_device_t devices[EC_NUM_DEVICES]; /**< EtherCAT devices. */
+ const uint8_t *macs[EC_NUM_DEVICES]; /**< Device MAC addresses. */
struct semaphore device_sem; /**< Device semaphore. */
+ ec_device_stats_t device_stats; /**< Device statistics. */
ec_fsm_master_t fsm; /**< Master state machine. */
ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
@@ -210,8 +240,8 @@
ext_datagram_queue. */
struct list_head external_datagram_queue; /**< External Datagram queue. */
- unsigned int send_interval; /**< Interval between calls to
- ecrt_master_send */
+ unsigned int send_interval; /**< Interval between two calls to
+ ecrt_master_send(). */
size_t max_queue_size; /**< Maximum size of datagram queue */
unsigned int debug_level; /**< Master debug level. */
@@ -310,6 +340,8 @@
void ec_master_internal_send_cb(void *);
void ec_master_internal_receive_cb(void *);
-/*****************************************************************************/
-
-#endif
+extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c
+
+/*****************************************************************************/
+
+#endif
--- a/master/module.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/module.c Thu Sep 06 18:28:57 2012 +0200
@@ -229,13 +229,19 @@
/*****************************************************************************/
+/** Maximum MAC string size.
+ */
+#define EC_MAX_MAC_STRING_SIZE (3 * ETH_ALEN)
+
/** Print a MAC address to a buffer.
*
+ * The buffer size must be at least EC_MAX_MAC_STRING_SIZE.
+ *
* \return number of bytes written.
*/
ssize_t ec_mac_print(
const uint8_t *mac, /**< MAC address */
- char *buffer /**< target buffer */
+ char *buffer /**< Target buffer. */
)
{
off_t off = 0;
@@ -289,7 +295,7 @@
/** Parse a MAC address from a string.
*
- * The MAC address must follow the regexp
+ * The MAC address must match the regular expression
* "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}".
*
* \return 0 on success, else < 0
@@ -303,8 +309,7 @@
if (!strlen(src)) {
if (allow_empty){
return 0;
- }
- else {
+ } else {
EC_ERR("MAC address may not be empty.\n");
return -EINVAL;
}
@@ -319,8 +324,9 @@
return -EINVAL;
}
mac[i] = value;
- if (i < ETH_ALEN - 1)
+ if (i < ETH_ALEN - 1) {
src = rem + 1; // skip colon
+ }
}
return 0;
@@ -445,6 +451,13 @@
* Device interface
*****************************************************************************/
+/** Device names.
+ */
+const char *ec_device_names[EC_NUM_DEVICES] = {
+ "main",
+ "backup"
+};
+
/** Offers an EtherCAT device to a certain master.
*
* The master decides, if it wants to use the device for EtherCAT operation,
@@ -462,47 +475,43 @@
)
{
ec_master_t *master;
- char str[20];
- unsigned int i;
+ char str[EC_MAX_MAC_STRING_SIZE];
+ unsigned int i, j;
for (i = 0; i < master_count; i++) {
master = &masters[i];
+ ec_mac_print(net_dev->dev_addr, str);
down(&master->device_sem);
- if (master->main_device.dev) { // master already has a device
- up(&master->device_sem);
- continue;
- }
-
- if (ec_mac_equal(master->main_mac, net_dev->dev_addr)
- || ec_mac_is_broadcast(master->main_mac)) {
- ec_mac_print(net_dev->dev_addr, str);
- EC_INFO("Accepting device %s for master %u.\n",
- str, master->index);
-
- ec_device_attach(&master->main_device, net_dev, poll, module);
- up(&master->device_sem);
-
- snprintf(net_dev->name, IFNAMSIZ, "ec%u", master->index);
-
- return &master->main_device; // offer accepted
- }
- else {
- up(&master->device_sem);
-
- if (master->debug_level) {
- ec_mac_print(net_dev->dev_addr, str);
- EC_MASTER_DBG(master, 0, "Master declined device %s.\n",
- str);
+
+ for (j = 0; j < EC_NUM_DEVICES; j++) {
+ if (!master->devices[j].dev
+ && (ec_mac_equal(master->macs[j], net_dev->dev_addr)
+ || ec_mac_is_broadcast(master->macs[j]))) {
+
+ EC_INFO("Accepting %s as %s device for master %u.\n",
+ str, ec_device_names[j], master->index);
+
+ ec_device_attach(&master->devices[j], net_dev, poll, module);
+ up(&master->device_sem);
+
+ snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
+ ec_device_names[j][0], master->index);
+
+ return &master->devices[j]; // offer accepted
}
}
+
+ up(&master->device_sem);
+
+ EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
}
return NULL; // offer declined
}
/******************************************************************************
- * Realtime interface
+ * Application interface
*****************************************************************************/
/** Request a master.
@@ -514,6 +523,7 @@
)
{
ec_master_t *master, *errptr = NULL;
+ unsigned int i, got_modules = 0;
EC_INFO("Requesting master %u...\n", master_index);
@@ -550,11 +560,17 @@
goto out_release;
}
- if (!try_module_get(master->main_device.module)) {
- up(&master->device_sem);
- EC_ERR("Device module is unloading!\n");
- errptr = ERR_PTR(-ENODEV);
- goto out_release;
+ for (i = 0; i < EC_NUM_DEVICES; i++) {
+ ec_device_t *device = &master->devices[i];
+ if (device->dev) {
+ if (!try_module_get(device->module)) {
+ up(&master->device_sem);
+ EC_MASTER_ERR(master, "Device module is unloading!\n");
+ errptr = ERR_PTR(-ENODEV);
+ goto out_module_put;
+ }
+ }
+ got_modules++;
}
up(&master->device_sem);
@@ -569,7 +585,12 @@
return master;
out_module_put:
- module_put(master->main_device.module);
+ for (; got_modules > 0; got_modules--) {
+ ec_device_t *device = &master->devices[i - 1];
+ if (device->dev) {
+ module_put(device->module);
+ }
+ }
out_release:
master->reserved = 0;
out_return:
@@ -588,6 +609,8 @@
void ecrt_release_master(ec_master_t *master)
{
+ unsigned int i;
+
EC_MASTER_INFO(master, "Releasing master...\n");
if (!master->reserved) {
@@ -598,7 +621,12 @@
ec_master_leave_operation_phase(master);
- module_put(master->main_device.module);
+ for (i = 0; i < EC_NUM_DEVICES; i++) {
+ if (master->devices[i].dev) {
+ module_put(master->devices[i].module);
+ }
+ }
+
master->reserved = 0;
EC_MASTER_INFO(master, "Released.\n");
--- a/master/slave.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/slave.c Thu Sep 06 18:28:57 2012 +0200
@@ -62,6 +62,7 @@
void ec_slave_init(
ec_slave_t *slave, /**< EtherCAT slave */
ec_master_t *master, /**< EtherCAT master */
+ ec_device_index_t dev_idx, /**< Device index. */
uint16_t ring_position, /**< ring position */
uint16_t station_address /**< station address to configure */
)
@@ -70,6 +71,7 @@
int ret;
slave->master = master;
+ slave->device_index = dev_idx;
slave->ring_position = ring_position;
slave->station_address = station_address;
slave->effective_alias = 0x0000;
--- a/master/slave.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/slave.h Thu Sep 06 18:28:57 2012 +0200
@@ -174,6 +174,8 @@
struct ec_slave
{
ec_master_t *master; /**< Master owning the slave. */
+ ec_device_index_t device_index; /**< Index of device the slave responds
+ on. */
// addresses
uint16_t ring_position; /**< Ring position. */
@@ -238,7 +240,8 @@
/*****************************************************************************/
// slave construction/destruction
-void ec_slave_init(ec_slave_t *, ec_master_t *, uint16_t, uint16_t);
+void ec_slave_init(ec_slave_t *, ec_master_t *, ec_device_index_t,
+ uint16_t, uint16_t);
void ec_slave_clear(ec_slave_t *);
void ec_slave_clear_sync_managers(ec_slave_t *);
--- a/master/slave_config.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/slave_config.c Thu Sep 06 18:28:57 2012 +0200
@@ -78,8 +78,6 @@
ec_sync_config_init(&sc->sync_configs[i]);
sc->used_fmmus = 0;
- sc->used_for_fmmu_datagram[EC_DIR_INPUT] = 0;
- sc->used_for_fmmu_datagram[EC_DIR_OUTPUT] = 0;
sc->dc_assign_activate = 0x0000;
sc->dc_sync[0].cycle_time = 0x00000000;
sc->dc_sync[1].cycle_time = 0x00000000;
--- a/master/slave_config.h Thu Sep 06 14:40:10 2012 +0200
+++ b/master/slave_config.h Thu Sep 06 18:28:57 2012 +0200
@@ -134,9 +134,6 @@
configurations. */
ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */
uint8_t used_fmmus; /**< Number of FMMUs used. */
- unsigned int used_for_fmmu_datagram[EC_DIR_COUNT]; /**< Number of FMMUs
- used for process data
- exchange datagrams. */
uint16_t dc_assign_activate; /**< Vendor-specific AssignActivate word. */
ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT]; /**< DC sync signals. */
--- a/master/voe_handler.c Thu Sep 06 14:40:10 2012 +0200
+++ b/master/voe_handler.c Thu Sep 06 18:28:57 2012 +0200
@@ -190,8 +190,9 @@
{
if (voe->config->slave) { // FIXME locking?
voe->state(voe);
- if (voe->request_state == EC_INT_REQUEST_BUSY)
+ if (voe->request_state == EC_INT_REQUEST_BUSY) {
ec_master_queue_datagram(voe->config->master, &voe->datagram);
+ }
} else {
voe->state = ec_voe_handler_state_error;
voe->request_state = EC_INT_REQUEST_FAILURE;
--- a/script/sysconfig/ethercat Thu Sep 06 14:40:10 2012 +0200
+++ b/script/sysconfig/ethercat Thu Sep 06 18:28:57 2012 +0200
@@ -4,10 +4,12 @@
#
# $Id$
#
+# vim: spelllang=en spell tw=78
+#
#------------------------------------------------------------------------------
#
-# Master devices.
+# Main Ethernet devices.
#
# The MASTER<X>_DEVICE variable specifies the Ethernet device for a master
# with index 'X'.
@@ -26,6 +28,15 @@
#MASTER1_DEVICE=""
#
+# Backup Ethernet devices
+#
+# The MASTER<X>_BACKUP variables specify the devices used for redundancy. They
+# behaves nearly the same as the MASTER<X>_DEVICE variable, except that it
+# does not interpret the ff:ff:ff:ff:ff:ff address.
+#
+#MASTER0_BACKUP=""
+
+#
# Ethernet driver modules to use for EtherCAT operation.
#
# Specify a non-empty list of Ethernet drivers, that shall be used for EtherCAT
--- a/tool/CommandDomains.cpp Thu Sep 06 14:40:10 2012 +0200
+++ b/tool/CommandDomains.cpp Thu Sep 06 18:28:57 2012 +0200
@@ -136,6 +136,11 @@
ec_ioctl_domain_fmmu_t fmmu;
unsigned int dataOffset;
string indent(doIndent ? " " : "");
+ unsigned int wc_sum = 0, dev_idx;
+
+ for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
+ wc_sum += domain.working_counter[dev_idx];
+ }
cout << indent << "Domain" << dec << domain.index << ":"
<< " LogBaseAddr 0x"
@@ -144,8 +149,14 @@
<< ", Size " << dec << setfill(' ')
<< setw(3) << domain.data_size
<< ", WorkingCounter "
- << domain.working_counter << "/"
- << domain.expected_working_counter << endl;
+ << wc_sum << "/"
+ << domain.expected_working_counter;
+ if (EC_NUM_DEVICES == 2) {
+ cout << " (" << domain.working_counter[EC_DEVICE_MAIN]
+ << "+" << domain.working_counter[EC_DEVICE_BACKUP]
+ << ")";
+ }
+ cout << endl;
if (!domain.data_size || getVerbosity() != Verbose)
return;
--- a/tool/CommandMaster.cpp Thu Sep 06 14:40:10 2012 +0200
+++ b/tool/CommandMaster.cpp Thu Sep 06 18:28:57 2012 +0200
@@ -93,7 +93,7 @@
<< " Phase: ";
switch (data.phase) {
- case 0: cout << "Waiting for device..."; break;
+ case 0: cout << "Waiting for device(s)..."; break;
case 1: cout << "Idle"; break;
case 2: cout << "Operation"; break;
default: cout << "???";
@@ -114,12 +114,6 @@
&& data.devices[i].address[5] == 0x00) {
cout << "None.";
} else {
- unsigned int lost =
- data.devices[i].tx_count - data.devices[i].rx_count;
- if (lost == 1) {
- // allow one frame travelling
- lost = 0;
- }
cout << hex << setfill('0')
<< setw(2) << (unsigned int) data.devices[i].address[0]
<< ":"
@@ -139,11 +133,12 @@
<< (data.devices[i].link_state ? "UP" : "DOWN") << endl
<< " Tx frames: "
<< data.devices[i].tx_count << endl
+ << " Tx bytes: "
+ << data.devices[i].tx_bytes << endl
<< " Rx frames: "
<< data.devices[i].rx_count << endl
- << " Lost frames: " << lost << endl
- << " Tx bytes: "
- << data.devices[i].tx_bytes << endl
+ << " Rx bytes: "
+ << data.devices[i].rx_bytes << endl
<< " Tx errors: "
<< data.devices[i].tx_errors << endl
<< " Tx frame rate [1/s]: "
@@ -166,33 +161,106 @@
}
}
cout << endl
- << " Loss rate [1/s]: "
- << setprecision(0) << fixed;
- for (j = 0; j < EC_RATE_COUNT; j++) {
- cout << setw(ColWidth)
- << data.devices[i].loss_rates[j] / 1000.0;
+ << " Rx frame rate [1/s]: "
+ << setfill(' ') << setprecision(0) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.devices[i].rx_frame_rates[j] / 1000.0;
if (j < EC_RATE_COUNT - 1) {
cout << " ";
}
}
cout << endl
- << " Frame loss [%]: "
+ << " Rx rate [KByte/s]: "
<< setprecision(1) << fixed;
for (j = 0; j < EC_RATE_COUNT; j++) {
- double perc = 0.0;
- if (data.devices[i].tx_frame_rates[j]) {
- perc = 100.0 * data.devices[i].loss_rates[j] /
- data.devices[i].tx_frame_rates[j];
- }
- cout << setw(ColWidth) << perc;
+ cout << setw(ColWidth)
+ << data.devices[i].rx_byte_rates[j] / 1024.0;
if (j < EC_RATE_COUNT - 1) {
cout << " ";
}
}
cout << setprecision(0) << endl;
}
- cout << endl;
- }
+ }
+ unsigned int lost = data.tx_count - data.rx_count;
+ if (lost == 1) {
+ // allow one frame travelling
+ lost = 0;
+ }
+ cout << " Common:" << endl
+ << " Tx frames: "
+ << data.tx_count << endl
+ << " Tx bytes: "
+ << data.tx_bytes << endl
+ << " Rx frames: "
+ << data.rx_count << endl
+ << " Rx bytes: "
+ << data.rx_bytes << endl
+ << " Lost frames: " << lost << endl
+ << " Tx frame rate [1/s]: "
+ << setfill(' ') << setprecision(0) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.tx_frame_rates[j] / 1000.0;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << endl
+ << " Tx rate [KByte/s]: "
+ << setprecision(1) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.tx_byte_rates[j] / 1024.0;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << endl
+ << " Rx frame rate [1/s]: "
+ << setfill(' ') << setprecision(0) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.rx_frame_rates[j] / 1000.0;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << endl
+ << " Rx rate [KByte/s]: "
+ << setprecision(1) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.rx_byte_rates[j] / 1024.0;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << endl
+ << " Loss rate [1/s]: "
+ << setprecision(0) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ cout << setw(ColWidth)
+ << data.loss_rates[j] / 1000.0;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << endl
+ << " Frame loss [%]: "
+ << setprecision(1) << fixed;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ double perc = 0.0;
+ if (data.tx_frame_rates[j]) {
+ perc = 100.0 * data.loss_rates[j] / data.tx_frame_rates[j];
+ }
+ cout << setw(ColWidth) << perc;
+ if (j < EC_RATE_COUNT - 1) {
+ cout << " ";
+ }
+ }
+ cout << setprecision(0) << endl;
cout << " Distributed clocks:" << endl
<< " Reference clock: ";
--- a/tool/CommandSlaves.cpp Thu Sep 06 14:40:10 2012 +0200
+++ b/tool/CommandSlaves.cpp Thu Sep 06 18:28:57 2012 +0200
@@ -144,7 +144,7 @@
)
{
ec_ioctl_master_t master;
- unsigned int i;
+ unsigned int i, lastDevice;
ec_ioctl_slave_t slave;
uint16_t lastAlias, aliasIndex;
Info info;
@@ -184,6 +184,7 @@
info.state = alStateString(slave.al_state);
info.flag = (slave.error_flag ? 'E' : '+');
+ info.device = slave.device_index;
if (strlen(slave.name)) {
info.name = slave.name;
@@ -215,7 +216,12 @@
cout << "Master" << dec << m.getIndex() << endl;
}
+ lastDevice = EC_DEVICE_MAIN;
for (iter = infoList.begin(); iter != infoList.end(); iter++) {
+ if (iter->device != lastDevice) {
+ lastDevice = iter->device;
+ cout << "xxx LINK FAILURE xxx" << endl;
+ }
cout << indent << setfill(' ') << right
<< setw(maxPosWidth) << iter->pos << " "
<< setw(maxAliasWidth) << iter->alias
@@ -245,6 +251,7 @@
cout << "Alias: " << si->alias << endl;
cout
+ << "Device: " << (si->device_index ? "Backup" : "Main") << endl
<< "State: " << alStateString(si->al_state) << endl
<< "Flag: " << (si->error_flag ? 'E' : '+') << endl
<< "Identity:" << endl
@@ -332,7 +339,8 @@
}
cout << " " << setw(10);
if (!si->ports[i].link.loop_closed) {
- cout << si->ports[i].receive_time - si->ports[0].receive_time;
+ cout << si->ports[i].receive_time -
+ si->ports[0].receive_time;
} else {
cout << "-";
}
@@ -406,7 +414,8 @@
<< " Enable SDO: "
<< (si->coe_details.enable_sdo ? "yes" : "no") << endl
<< " Enable SDO Info: "
- << (si->coe_details.enable_sdo_info ? "yes" : "no") << endl
+ << (si->coe_details.enable_sdo_info ? "yes" : "no")
+ << endl
<< " Enable PDO Assign: "
<< (si->coe_details.enable_pdo_assign
? "yes" : "no") << endl
--- a/tool/CommandSlaves.h Thu Sep 06 14:40:10 2012 +0200
+++ b/tool/CommandSlaves.h Thu Sep 06 18:28:57 2012 +0200
@@ -51,6 +51,7 @@
string state;
string flag;
string name;
+ unsigned int device;
};
void listSlaves(MasterDevice &, const SlaveList &, bool);