Merged redundancy branch to stable-1.5.

This commit is contained in:
Florian Pose 2012-09-06 18:28:57 +02:00
commit 1c8790c483
33 changed files with 1550 additions and 444 deletions

4
NEWS
View File

@ -37,7 +37,7 @@ Changes in 1.5.0:
- 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 @@ Changes in 1.5.0:
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:

View File

@ -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 @@ typedef struct ec_voe_handler ec_voe_handler_t; /**< \see ec_voe_handler. */
* \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 @@ typedef struct {
- 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 enum {
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 @@ void ecrt_master_send_ext(
/** 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

View File

@ -256,8 +256,9 @@ int ecrt_master_get_sync_manager(ec_master_t *master, uint16_t slave_position,
{
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 @@ void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
/*****************************************************************************/
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;

View File

@ -36,6 +36,7 @@ obj-m := ec_master.o
ec_master-objs := \
cdev.o \
datagram.o \
datagram_pair.o \
device.o \
domain.o \
fmmu_config.o \

View File

@ -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 \

View File

@ -190,10 +190,11 @@ int ec_cdev_ioctl_master(
)
{
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 @@ int ec_cdev_ioctl_master(
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_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];
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];
}
}
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;
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[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 @@ int ec_cdev_ioctl_slave(
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 @@ int ec_cdev_ioctl_domain(
{
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 @@ int ec_cdev_ioctl_domain(
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);
@ -1816,6 +1827,40 @@ int ec_cdev_ioctl_master_state(
/*****************************************************************************/
/** 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(
@ -3618,6 +3663,9 @@ long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
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;

View File

@ -88,6 +88,7 @@ static const char *type_strings[] = {
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;
@ -426,13 +427,70 @@ int ec_datagram_brw(
/*****************************************************************************/
/** 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 @@ int ec_datagram_lrd(
/*****************************************************************************/
/** 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 @@ int ec_datagram_lwr(
/*****************************************************************************/
/** 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. */

View File

@ -85,9 +85,10 @@ typedef enum {
/** 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_frmw(ec_datagram_t *, uint16_t, uint16_t, size_t);
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 *);

173
master/datagram_pair.c Normal file
View File

@ -0,0 +1,173 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
*****************************************************************************/
/**
\file
EtherCAT datagram pair methods.
*/
/*****************************************************************************/
#include <linux/slab.h>
#include "master.h"
#include "datagram_pair.h"
/*****************************************************************************/
/** Datagram pair constructor.
*/
int ec_datagram_pair_init(
ec_datagram_pair_t *pair, /**< Datagram pair. */
ec_domain_t *domain, /**< Parent domain. */
uint32_t logical_offset,
uint8_t *data,
size_t data_size, /**< Data size. */
const unsigned int used[] /**< input/output use count. */
)
{
ec_device_index_t dev_idx;
int ret;
INIT_LIST_HEAD(&pair->list);
pair->domain = domain;
for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_datagram_init(&pair->datagrams[dev_idx]);
snprintf(pair->datagrams[dev_idx].name,
EC_DATAGRAM_NAME_SIZE, "domain%u-%u-%s", domain->index,
logical_offset, ec_device_names[dev_idx]);
pair->datagrams[dev_idx].device_index = dev_idx;
}
pair->expected_working_counter = 0U;
/* backup datagram has its own memory */
ret = ec_datagram_prealloc(&pair->datagrams[EC_DEVICE_BACKUP],
data_size);
if (ret) {
goto out_datagrams;
}
if (!(pair->send_buffer = kmalloc(data_size, GFP_KERNEL))) {
EC_MASTER_ERR(domain->master,
"Failed to allocate domain send buffer!\n");
ret = -ENOMEM;
goto out_datagrams;
}
/* The ec_datagram_lxx() calls below can not fail, because either the
* datagram has external memory or it is preallocated. */
if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs
ec_datagram_lrw_ext(&pair->datagrams[EC_DEVICE_MAIN],
logical_offset, data_size, data);
ec_datagram_lrw(&pair->datagrams[EC_DEVICE_BACKUP],
logical_offset, data_size);
// If LRW is used, output FMMUs increment the working counter by 2,
// while input FMMUs increment it by 1.
pair->expected_working_counter =
used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT];
} else if (used[EC_DIR_OUTPUT]) { // outputs only
ec_datagram_lwr_ext(&pair->datagrams[EC_DEVICE_MAIN],
logical_offset, data_size, data);
ec_datagram_lwr(&pair->datagrams[EC_DEVICE_BACKUP],
logical_offset, data_size);
pair->expected_working_counter = used[EC_DIR_OUTPUT];
} else { // inputs only (or nothing)
ec_datagram_lrd_ext(&pair->datagrams[EC_DEVICE_MAIN],
logical_offset, data_size, data);
ec_datagram_lrd(&pair->datagrams[EC_DEVICE_BACKUP],
logical_offset, data_size);
pair->expected_working_counter = used[EC_DIR_INPUT];
}
for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_datagram_zero(&pair->datagrams[dev_idx]);
}
return 0;
out_datagrams:
for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_datagram_clear(&pair->datagrams[dev_idx]);
}
return ret;
}
/*****************************************************************************/
/** Datagram pair destructor.
*/
void ec_datagram_pair_clear(
ec_datagram_pair_t *pair /**< Datagram pair. */
)
{
unsigned int dev_idx;
for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_datagram_clear(&pair->datagrams[dev_idx]);
}
if (pair->send_buffer) {
kfree(pair->send_buffer);
}
}
/*****************************************************************************/
/** Process received data.
*/
uint16_t ec_datagram_pair_process(
ec_datagram_pair_t *pair, /**< Datagram pair. */
uint16_t wc_sum[EC_NUM_DEVICES] /**< Working counter sums. */
)
{
unsigned int dev_idx;
uint16_t pair_wc = 0;
for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_datagram_t *datagram = &pair->datagrams[dev_idx];
ec_datagram_output_stats(datagram);
if (datagram->state == EC_DATAGRAM_RECEIVED) {
pair_wc += datagram->working_counter;
wc_sum[dev_idx] += datagram->working_counter;
}
}
return pair_wc;
}
/*****************************************************************************/

69
master/datagram_pair.h Normal file
View File

@ -0,0 +1,69 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
*****************************************************************************/
/**
\file
EtherCAT datagram pair structure.
*/
/*****************************************************************************/
#ifndef __EC_DATAGRAM_PAIR_H__
#define __EC_DATAGRAM_PAIR_H__
#include <linux/list.h>
#include "globals.h"
#include "datagram.h"
/*****************************************************************************/
/** Domain datagram pair.
*/
typedef struct {
struct list_head list; /**< List header. */
ec_domain_t *domain;
ec_datagram_t datagrams[EC_NUM_DEVICES]; /**< Main and backup datagram.
*/
uint8_t *send_buffer;
unsigned int expected_working_counter; /**< Expectord working conter. */
} ec_datagram_pair_t;
/*****************************************************************************/
int ec_datagram_pair_init(ec_datagram_pair_t *, ec_domain_t *, uint32_t,
uint8_t *, size_t, const unsigned int []);
void ec_datagram_pair_clear(ec_datagram_pair_t *);
uint16_t ec_datagram_pair_process(ec_datagram_pair_t *,
uint16_t[EC_NUM_DEVICES]);
/*****************************************************************************/
#endif

View File

@ -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 @@ void ec_device_send(
{
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 @@ void ec_device_send(
#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 @@ void ec_device_clear_stats(
// zero frame statistics
device->tx_count = 0;
device->rx_count = 0;
device->tx_errors = 0;
device->tx_bytes = 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->last_loss = 0;
device->rx_bytes = 0;
device->last_rx_bytes = 0;
device->tx_errors = 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 @@ void ec_device_poll(
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 ec_device_poll(
void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
{
ec_master_t *master = device->master;
char str[20];
char dev_str[20], mac_str[20];
ec_mac_print(device->dev->dev_addr, str);
EC_MASTER_INFO(master, "Releasing main device %s.\n", str);
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 @@ void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
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 @@ int ecdev_open(ec_device_t *device /**< EtherCAT device */)
*/
void ecdev_close(ec_device_t *device /**< EtherCAT device */)
{
ec_master_leave_idle_phase(device->master);
ec_master_t *master = device->master;
if (ec_device_close(device))
EC_MASTER_WARN(device->master, "Failed to close device!\n");
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 @@ void ecdev_receive(
}
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 @@ void ecdev_set_link(
)
{
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 @@ uint8_t ecdev_get_link(
)
{
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;
}

View File

@ -101,19 +101,24 @@ struct ec_device
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 @@ void ec_device_poll(ec_device_t *);
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,

View File

@ -41,6 +41,9 @@
#include "slave_config.h"
#include "domain.h"
#include "datagram_pair.h"
#define DEBUG_REDUNDANCY 0
/*****************************************************************************/
@ -63,10 +66,12 @@ void ec_domain_init(
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_init(
*/
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 @@ void ec_domain_clear_data(
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 @@ void ec_domain_add_fmmu_config(
/*****************************************************************************/
/** Allocates a domain datagram and appends it to the list.
/** Allocates a domain datagram pair 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.
* 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. */
const unsigned int used[] /**< Slave config counter for in/out. */
)
{
ec_datagram_t *datagram;
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];
ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data,
data_size, used);
if (ret) {
kfree(datagram_pair);
return ret;
}
ec_datagram_zero(datagram);
list_add_tail(&datagram->list, &domain->datagrams);
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 @@ int ec_domain_finish(
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 @@ int ec_domain_finish(
}
}
// 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 @@ int ec_domain_finish(
" %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 @@ const ec_fmmu_config_t *ec_domain_find_fmmu(
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 @@ int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain,
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 @@ uint8_t *ecrt_domain_data(ec_domain_t *domain)
void ecrt_domain_process(ec_domain_t *domain)
{
uint16_t working_counter_sum;
ec_datagram_t *datagram;
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;
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 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
}
}
}
if (working_counter_sum != domain->working_counter) {
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 @@ void ecrt_domain_process(ec_domain_t *domain)
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_process(ec_domain_t *domain)
void ecrt_domain_queue(ec_domain_t *domain)
{
ec_datagram_t *datagram;
ec_datagram_pair_t *datagram_pair;
ec_device_index_t dev_idx;
list_for_each_entry(datagram, &domain->datagrams, list) {
ec_master_queue_datagram(domain->master, datagram);
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_queue(ec_domain_t *domain)
void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
{
state->working_counter = domain->working_counter;
state->working_counter =
domain->working_counter[EC_DEVICE_MAIN]
+ domain->working_counter[EC_DEVICE_BACKUP];
if (domain->working_counter) {
if (domain->working_counter == domain->expected_working_counter) {
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 @@ void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
} else {
state->wc_state = EC_WC_ZERO;
}
state->redundancy_active = domain->redundancy_active;
}
/*****************************************************************************/

View File

@ -63,12 +63,15 @@ struct ec_domain
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. */
};

View File

@ -384,11 +384,13 @@ int ec_eoe_is_idle(const ec_eoe_t *eoe /**< EoE handler */)
*
* 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 @@ void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */)
*
* 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 @@ void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
#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;

View File

@ -67,6 +67,7 @@ void ec_fsm_master_state_sdo_dictionary(ec_fsm_master_t *);
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 @@ void ec_fsm_master_init(
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 @@ void ec_fsm_master_restart(
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 @@ void ec_fsm_master_state_start(
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 @@ void ec_fsm_master_state_broadcast(
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);
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 && !master->main_device.link_state) {
// link went down
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;
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 (fsm->slaves_responding) {
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 @@ void ec_fsm_master_state_broadcast(
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 @@ void ec_fsm_master_state_broadcast(
#endif
ec_master_clear_slaves(master);
master->slave_count = fsm->slaves_responding;
for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES;
dev_idx++) {
count += fsm->slaves_responding[dev_idx];
}
if (!master->slave_count) {
if (!count) {
// no slaves present -> finish state machine.
master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue);
@ -269,12 +293,11 @@ void ec_fsm_master_state_broadcast(
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 @@ void ec_fsm_master_state_broadcast(
}
// 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;
}
}
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++;
}
// 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;
ec_fsm_master_enter_clear_addresses(fsm);
return;
}
}
@ -318,6 +355,7 @@ void ec_fsm_master_state_broadcast(
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 @@ int ec_fsm_master_action_process_register(
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 @@ void ec_fsm_master_action_idle(
ec_slave_t *slave;
// Check for pending internal SDO requests
if (ec_fsm_master_action_process_sdo(fsm))
if (ec_fsm_master_action_process_sdo(fsm)) {
return;
}
// enable processing of requests
for (slave = master->slaves;
@ -518,6 +558,7 @@ void ec_fsm_master_action_idle(
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 @@ void ec_fsm_master_action_next_slave_state(
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 @@ void ec_fsm_master_action_configure(
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 @@ void ec_fsm_master_state_acknowledge(
{
ec_slave_t *slave = fsm->slave;
if (ec_fsm_change_exec(&fsm->fsm_change))
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 @@ void ec_fsm_master_state_acknowledge(
/*****************************************************************************/
/** 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 @@ void ec_fsm_master_state_clear_addresses(
ec_master_t *master = fsm->master;
ec_datagram_t *datagram = fsm->datagram;
if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
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 @@ void ec_fsm_master_state_clear_addresses(
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 @@ void ec_fsm_master_state_dc_measure_delays(
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 @@ void ec_fsm_master_state_dc_measure_delays(
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 @@ void ec_fsm_master_state_scan_slave(
#ifdef EC_EOE
ec_slave_t *slave = fsm->slave;
#endif
if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan))
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 @@ void ec_fsm_master_state_scan_slave(
// 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 @@ void ec_fsm_master_state_configure_slave(
{
ec_master_t *master = fsm->master;
if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config))
if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config)) {
return;
}
fsm->slave->force_config = 0;
@ -886,6 +973,7 @@ void ec_fsm_master_enter_write_system_times(
// 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 @@ void ec_fsm_master_state_dc_read_offset(
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 @@ void ec_fsm_master_state_sdo_dictionary(
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);

View File

@ -115,12 +115,19 @@ struct ec_fsm_master {
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 */

View File

@ -145,16 +145,19 @@ void ec_fsm_slave_state_ready(
)
{
// Check for pending external SDO requests
if (ec_fsm_slave_action_process_sdo(fsm))
if (ec_fsm_slave_action_process_sdo(fsm)) {
return;
}
// Check for pending FoE requests
if (ec_fsm_slave_action_process_foe(fsm))
if (ec_fsm_slave_action_process_foe(fsm)) {
return;
}
// Check for pending SoE requests
if (ec_fsm_slave_action_process_soe(fsm))
if (ec_fsm_slave_action_process_soe(fsm)) {
return;
}
}
/*****************************************************************************/

View File

@ -196,6 +196,16 @@ enum {
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.

View File

@ -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 @@ typedef struct {
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 @@ typedef struct {
uint16_t position;
// outputs
unsigned int device_index;
uint32_t vendor_id;
uint32_t product_code;
uint32_t revision_number;
@ -279,7 +292,7 @@ typedef struct {
// 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;
@ -659,6 +672,16 @@ typedef struct {
/*****************************************************************************/
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;

View File

@ -82,6 +82,12 @@ static unsigned long ext_injection_timeout_jiffies;
#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_operation_thread(void *);
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 @@ void ec_master_init_static(void)
{
#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 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
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 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
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 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
// 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 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
// 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 @@ out_clear_fsm:
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 @@ void ec_master_clear(
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 ec_master_enter_idle_phase(
)
{
int ret;
ec_device_index_t dev_idx;
EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n");
@ -551,7 +565,9 @@ int ec_master_enter_idle_phase(
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 @@ void ec_master_leave_idle_phase(ec_master_t *master /**< EtherCAT master */)
/** 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 @@ int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
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 @@ void ec_master_inject_external_datagrams(
#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 @@ void ec_master_queue_datagram_ext(
/*****************************************************************************/
/** 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 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
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 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
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 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
// 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 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
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 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
#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 @@ void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
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 @@ void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
// dequeue the received datagram
datagram->state = EC_DATAGRAM_RECEIVED;
#ifdef EC_HAVE_CYCLES
datagram->cycles_received = master->main_device.cycles_poll;
datagram->cycles_received =
master->devices[EC_DEVICE_MAIN].cycles_poll;
#endif
datagram->jiffies_received = master->main_device.jiffies_poll;
datagram->jiffies_received =
master->devices[EC_DEVICE_MAIN].jiffies_poll;
list_del_init(&datagram->queue);
}
}
@ -1155,6 +1190,83 @@ void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */)
}
}
/*****************************************************************************/
/** 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 @@ static int ec_master_idle_thread(void *priv_data)
}
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_domain_t *ecrt_master_create_domain_err(
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_deactivate(ec_master_t *master)
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);
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;
}
// 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, dev_idx);
}
// send frames
ec_master_send_datagrams(master);
}
/*****************************************************************************/
@ -2150,18 +2273,22 @@ void ecrt_master_receive(ec_master_t *master)
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 @@ void ecrt_master_receive(ec_master_t *master)
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 @@ int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
" 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_callbacks(ec_master_t *master,
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);
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);

View File

@ -137,6 +137,37 @@ typedef struct {
/*****************************************************************************/
/** 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 ec_master {
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 @@ struct ec_master {
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_request_op(ec_master_t *);
void ec_master_internal_send_cb(void *);
void ec_master_internal_receive_cb(void *);
extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c
/*****************************************************************************/
#endif

View File

@ -229,13 +229,19 @@ int ec_mac_equal(
/*****************************************************************************/
/** 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 @@ int ec_mac_is_broadcast(
/** 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 @@ static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty)
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 @@ static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty)
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 @@ size_t ec_state_string(uint8_t states, /**< slave states */
* 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_device_t *ecdev_offer(
)
{
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);
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_device_attach(&master->main_device, net_dev, poll, module);
up(&master->device_sem);
EC_INFO("Accepting %s as %s device for master %u.\n",
str, ec_device_names[j], master->index);
snprintf(net_dev->name, IFNAMSIZ, "ec%u", master->index);
ec_device_attach(&master->devices[j], net_dev, poll, module);
up(&master->device_sem);
return &master->main_device; // offer accepted
}
else {
up(&master->device_sem);
snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
ec_device_names[j][0], master->index);
if (master->debug_level) {
ec_mac_print(net_dev->dev_addr, str);
EC_MASTER_DBG(master, 0, "Master declined device %s.\n",
str);
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 *ecrt_request_master_err(
)
{
ec_master_t *master, *errptr = NULL;
unsigned int i, got_modules = 0;
EC_INFO("Requesting master %u...\n", master_index);
@ -550,11 +560,17 @@ ec_master_t *ecrt_request_master_err(
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 @@ ec_master_t *ecrt_request_master_err(
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 @@ ec_master_t *ecrt_request_master(unsigned int master_index)
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 @@ void ecrt_release_master(ec_master_t *master)
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");

View File

@ -62,6 +62,7 @@ char *ec_slave_sii_string(ec_slave_t *, unsigned int);
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 @@ void ec_slave_init(
int ret;
slave->master = master;
slave->device_index = dev_idx;
slave->ring_position = ring_position;
slave->station_address = station_address;
slave->effective_alias = 0x0000;

View File

@ -174,6 +174,8 @@ typedef struct {
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 @@ struct ec_slave
/*****************************************************************************/
// 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 *);

View File

@ -78,8 +78,6 @@ void ec_slave_config_init(
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;

View File

@ -134,9 +134,6 @@ struct ec_slave_config {
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. */

View File

@ -190,8 +190,9 @@ ec_request_state_t ecrt_voe_handler_execute(ec_voe_handler_t *voe)
{
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;

View File

@ -4,10 +4,12 @@
#
# $Id$
#
# vim: spelllang=en spell tw=78
#
#------------------------------------------------------------------------------
#
# Master devices.
# Main Ethernet devices.
#
# The MASTER<X>_DEVICE variable specifies the Ethernet device for a master
# with index 'X'.
@ -25,6 +27,15 @@
MASTER0_DEVICE=""
#MASTER1_DEVICE=""
#
# Backup Ethernet devices
#
# The MASTER<X>_BACKUP variables specify the devices used for redundancy. They
# behaves nearly the same as the MASTER<X>_DEVICE variable, except that it
# does not interpret the ff:ff:ff:ff:ff:ff address.
#
#MASTER0_BACKUP=""
#
# Ethernet driver modules to use for EtherCAT operation.
#

View File

@ -136,6 +136,11 @@ void CommandDomains::showDomain(
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 @@ void CommandDomains::showDomain(
<< ", 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;

View File

@ -93,7 +93,7 @@ void CommandMaster::execute(const StringVector &args)
<< " 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 @@ void CommandMaster::execute(const StringVector &args)
&& 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 @@ void CommandMaster::execute(const StringVector &args)
<< (data.devices[i].link_state ? "UP" : "DOWN") << endl
<< " Tx frames: "
<< data.devices[i].tx_count << endl
<< " Rx frames: "
<< data.devices[i].rx_count << endl
<< " Lost frames: " << lost << endl
<< " Tx bytes: "
<< data.devices[i].tx_bytes << endl
<< " Rx frames: "
<< data.devices[i].rx_count << 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 @@ void CommandMaster::execute(const StringVector &args)
}
}
cout << endl
<< " Loss rate [1/s]: "
<< setprecision(0) << fixed;
<< " Rx frame rate [1/s]: "
<< setfill(' ') << setprecision(0) << fixed;
for (j = 0; j < EC_RATE_COUNT; j++) {
cout << setw(ColWidth)
<< data.devices[i].loss_rates[j] / 1000.0;
<< 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: ";

View File

@ -144,7 +144,7 @@ void CommandSlaves::listSlaves(
)
{
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 @@ void CommandSlaves::listSlaves(
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 @@ void CommandSlaves::listSlaves(
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 @@ void CommandSlaves::showSlaves(
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 @@ void CommandSlaves::showSlaves(
}
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 @@ void CommandSlaves::showSlaves(
<< " 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

View File

@ -51,6 +51,7 @@ class CommandSlaves:
string state;
string flag;
string name;
unsigned int device;
};
void listSlaves(MasterDevice &, const SlaveList &, bool);