# HG changeset patch # User Florian Pose # Date 1346948937 -7200 # Node ID fdb85a80658527570a382a3bd65692248d1e4b82 # Parent 63bef67e812b956365c9ea0c9fd69b53d1394663# Parent 8cbb68315d29ab941650ec0e905052804f838cbd Merged redundancy branch to stable-1.5. diff -r 63bef67e812b -r fdb85a806585 NEWS --- 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: diff -r 63bef67e812b -r fdb85a806585 include/ecrt.h --- 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 diff -r 63bef67e812b -r fdb85a806585 lib/master.c --- 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; diff -r 63bef67e812b -r fdb85a806585 master/Kbuild.in --- 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 \ diff -r 63bef67e812b -r fdb85a806585 master/Makefile.am --- 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 \ diff -r 63bef67e812b -r fdb85a806585 master/cdev.c --- 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; diff -r 63bef67e812b -r fdb85a806585 master/datagram.c --- 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. */ diff -r 63bef67e812b -r fdb85a806585 master/datagram.h --- 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 *); diff -r 63bef67e812b -r fdb85a806585 master/datagram_pair.c --- /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 + +#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; +} + +/*****************************************************************************/ diff -r 63bef67e812b -r fdb85a806585 master/datagram_pair.h --- /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 + +#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 diff -r 63bef67e812b -r fdb85a806585 master/device.c --- 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; } diff -r 63bef67e812b -r fdb85a806585 master/device.h --- 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, diff -r 63bef67e812b -r fdb85a806585 master/domain.c --- 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; } /*****************************************************************************/ diff -r 63bef67e812b -r fdb85a806585 master/domain.h --- 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. */ }; diff -r 63bef67e812b -r fdb85a806585 master/ethernet.c --- 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; diff -r 63bef67e812b -r fdb85a806585 master/fsm_master.c --- 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); diff -r 63bef67e812b -r fdb85a806585 master/fsm_master.h --- 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 */ diff -r 63bef67e812b -r fdb85a806585 master/fsm_slave.c --- 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; + } } /*****************************************************************************/ diff -r 63bef67e812b -r fdb85a806585 master/globals.h --- 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. diff -r 63bef67e812b -r fdb85a806585 master/ioctl.h --- 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; diff -r 63bef67e812b -r fdb85a806585 master/master.c --- 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); diff -r 63bef67e812b -r fdb85a806585 master/master.h --- 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 diff -r 63bef67e812b -r fdb85a806585 master/module.c --- 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"); diff -r 63bef67e812b -r fdb85a806585 master/slave.c --- 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; diff -r 63bef67e812b -r fdb85a806585 master/slave.h --- 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 *); diff -r 63bef67e812b -r fdb85a806585 master/slave_config.c --- 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; diff -r 63bef67e812b -r fdb85a806585 master/slave_config.h --- 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. */ diff -r 63bef67e812b -r fdb85a806585 master/voe_handler.c --- 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; diff -r 63bef67e812b -r fdb85a806585 script/sysconfig/ethercat --- 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_DEVICE variable specifies the Ethernet device for a master # with index 'X'. @@ -26,6 +28,15 @@ #MASTER1_DEVICE="" # +# Backup Ethernet devices +# +# The MASTER_BACKUP variables specify the devices used for redundancy. They +# behaves nearly the same as the MASTER_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 diff -r 63bef67e812b -r fdb85a806585 tool/CommandDomains.cpp --- 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; diff -r 63bef67e812b -r fdb85a806585 tool/CommandMaster.cpp --- 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: "; diff -r 63bef67e812b -r fdb85a806585 tool/CommandSlaves.cpp --- 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 diff -r 63bef67e812b -r fdb85a806585 tool/CommandSlaves.h --- 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);