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.29, thanks to Andre Puschmann.
- Added e100 driver for 2.6.31. - Added e100 driver for 2.6.31.
- Added e100 driver for 2.6.32. - 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 2.6.37.
- Added e100 driver for 3.0. - Added e100 driver for 3.0.
* Added 8139too driver for kernels 2.6.25 (F. Pose), 2.6.26 (M. Luescher), * 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'. supports ranges like '0,3,8-10'.
* A sync manager is always enabled, if it contains registered process data. * A sync manager is always enabled, if it contains registered process data.
* Added a configuration switch --enable-wildcards to use 0xffffffff as a * 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. * Added support for systemd.
Changes in 1.4.0: Changes in 1.4.0:

View File

@ -39,6 +39,14 @@
* request a master, to map process data, to communicate with slaves via CoE * request a master, to map process data, to communicate with slaves via CoE
* and to configure and activate the bus. * 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: * Changes in version 1.5:
* *
* - Added the distributed clocks feature and the respective method * - 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) #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. /** 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(). * \see ecrt_master_state().
*/ */
typedef struct { 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. unsigned int al_states : 4; /**< Application-layer states of all slaves.
The states are coded in the lower 4 bits. The states are coded in the lower 4 bits.
If a bit is set, it means that at least one If a bit is set, it means that at least one
@ -189,11 +209,36 @@ typedef struct {
- Bit 1: \a PREOP - Bit 1: \a PREOP
- Bit 2: \a SAFEOP - Bit 2: \a SAFEOP
- Bit 3: \a OP */ - 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; } 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. /** Slave configuration state.
* *
* This is used as an output parameter of ecrt_slave_config_state(). * This is used as an output parameter of ecrt_slave_config_state().
@ -304,6 +349,7 @@ typedef enum {
typedef struct { typedef struct {
unsigned int working_counter; /**< Value of the last working counter. */ unsigned int working_counter; /**< Value of the last working counter. */
ec_wc_state_t wc_state; /**< Working counter interpretation. */ ec_wc_state_t wc_state; /**< Working counter interpretation. */
unsigned int redundancy_active; /**< Redundant link is in use. */
} ec_domain_state_t; } ec_domain_state_t;
/*****************************************************************************/ /*****************************************************************************/
@ -848,12 +894,29 @@ void ecrt_master_send_ext(
/** Reads the current master state. /** Reads the current master state.
* *
* Stores the master state information in the given \a state structure. * 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( void ecrt_master_state(
const ec_master_t *master, /**< EtherCAT master. */ const ec_master_t *master, /**< EtherCAT master. */
ec_master_state_t *state /**< Structure to store the information. */ 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. /** Sets the application time.
* *
* The master has to know the application's time when operating slaves with * 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; ec_ioctl_slave_sync_t data;
if (sync_index >= EC_MAX_SYNC_MANAGERS) if (sync_index >= EC_MAX_SYNC_MANAGERS) {
return -ENOENT; return -ENOENT;
}
memset(&data, 0x00, sizeof(ec_ioctl_slave_sync_t)); memset(&data, 0x00, sizeof(ec_ioctl_slave_sync_t));
data.slave_position = slave_position; 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) void ecrt_master_application_time(ec_master_t *master, uint64_t app_time)
{ {
ec_ioctl_app_time_t data; ec_ioctl_app_time_t data;

View File

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

View File

@ -31,6 +31,7 @@
noinst_HEADERS = \ noinst_HEADERS = \
cdev.c cdev.h \ cdev.c cdev.h \
datagram.c datagram.h \ datagram.c datagram.h \
datagram_pair.c datagram_pair.h \
debug.c debug.h \ debug.c debug.h \
device.c device.h \ device.c device.h \
domain.c domain.h \ domain.c domain.h \

View File

@ -190,10 +190,11 @@ int ec_cdev_ioctl_master(
) )
{ {
ec_ioctl_master_t data; ec_ioctl_master_t data;
unsigned int i; unsigned int i, j;
if (down_interruptible(&master->master_sem)) if (down_interruptible(&master->master_sem))
return -EINTR; return -EINTR;
data.slave_count = master->slave_count; data.slave_count = master->slave_count;
data.config_count = ec_master_config_count(master); data.config_count = ec_master_config_count(master);
data.domain_count = ec_master_domain_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.phase = (uint8_t) master->phase;
data.active = (uint8_t) master->active; data.active = (uint8_t) master->active;
data.scan_busy = master->scan_busy; data.scan_busy = master->scan_busy;
up(&master->master_sem); up(&master->master_sem);
if (down_interruptible(&master->device_sem)) if (down_interruptible(&master->device_sem))
return -EINTR; return -EINTR;
if (master->main_device.dev) { for (i = 0; i < EC_NUM_DEVICES; i++) {
memcpy(data.devices[0].address, ec_device_t *device = &master->devices[i];
master->main_device.dev->dev_addr, ETH_ALEN);
} else { if (device->dev) {
memcpy(data.devices[0].address, master->main_mac, ETH_ALEN); memcpy(data.devices[i].address,
} device->dev->dev_addr, ETH_ALEN);
data.devices[0].attached = master->main_device.dev ? 1 : 0; } else {
data.devices[0].link_state = master->main_device.link_state ? 1 : 0; memcpy(data.devices[i].address, master->macs[i], ETH_ALEN);
data.devices[0].tx_count = master->main_device.tx_count; }
data.devices[0].rx_count = master->main_device.rx_count; data.devices[i].attached = device->dev ? 1 : 0;
data.devices[0].tx_bytes = master->main_device.tx_bytes; data.devices[i].link_state = device->link_state ? 1 : 0;
data.devices[0].tx_errors = master->main_device.tx_errors; data.devices[i].tx_count = device->tx_count;
for (i = 0; i < EC_RATE_COUNT; i++) { data.devices[i].rx_count = device->rx_count;
data.devices[0].tx_frame_rates[i] = data.devices[i].tx_bytes = device->tx_bytes;
master->main_device.tx_frame_rates[i]; data.devices[i].rx_bytes = device->rx_bytes;
data.devices[0].tx_byte_rates[i] = data.devices[i].tx_errors = device->tx_errors;
master->main_device.tx_byte_rates[i]; for (j = 0; j < EC_RATE_COUNT; j++) {
data.devices[0].loss_rates[i] = master->main_device.loss_rates[i]; 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) { data.tx_count = master->device_stats.tx_count;
memcpy(data.devices[1].address, data.rx_count = master->device_stats.rx_count;
master->backup_device.dev->dev_addr, ETH_ALEN); data.tx_bytes = master->device_stats.tx_bytes;
} else { data.rx_bytes = master->device_stats.rx_bytes;
memcpy(data.devices[1].address, master->backup_mac, ETH_ALEN);
}
data.devices[1].attached = master->backup_device.dev ? 1 : 0;
data.devices[1].link_state = master->backup_device.link_state ? 1 : 0;
data.devices[1].tx_count = master->backup_device.tx_count;
data.devices[1].rx_count = master->backup_device.rx_count;
data.devices[1].tx_bytes = master->backup_device.tx_bytes;
data.devices[1].tx_errors = master->backup_device.tx_errors;
for (i = 0; i < EC_RATE_COUNT; i++) { for (i = 0; i < EC_RATE_COUNT; i++) {
data.devices[1].tx_frame_rates[i] = data.tx_frame_rates[i] =
master->backup_device.tx_frame_rates[i]; master->device_stats.tx_frame_rates[i];
data.devices[1].tx_byte_rates[i] = data.rx_frame_rates[i] =
master->backup_device.tx_byte_rates[i]; master->device_stats.rx_frame_rates[i];
data.devices[1].loss_rates[i] = master->backup_device.loss_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); up(&master->device_sem);
@ -287,6 +294,7 @@ int ec_cdev_ioctl_slave(
return -EINVAL; return -EINVAL;
} }
data.device_index = slave->device_index;
data.vendor_id = slave->sii.vendor_id; data.vendor_id = slave->sii.vendor_id;
data.product_code = slave->sii.product_code; data.product_code = slave->sii.product_code;
data.revision_number = slave->sii.revision_number; data.revision_number = slave->sii.revision_number;
@ -530,6 +538,7 @@ int ec_cdev_ioctl_domain(
{ {
ec_ioctl_domain_t data; ec_ioctl_domain_t data;
const ec_domain_t *domain; const ec_domain_t *domain;
unsigned int dev_idx;
if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
return -EFAULT; return -EFAULT;
@ -546,7 +555,9 @@ int ec_cdev_ioctl_domain(
data.data_size = domain->data_size; data.data_size = domain->data_size;
data.logical_base_address = domain->logical_base_address; 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.expected_working_counter = domain->expected_working_counter;
data.fmmu_count = ec_domain_fmmu_count(domain); 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. /** Get the master state.
*/ */
int ec_cdev_ioctl_app_time( 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: case EC_IOCTL_MASTER_STATE:
ret = ec_cdev_ioctl_master_state(master, arg, priv); ret = ec_cdev_ioctl_master_state(master, arg, priv);
break; break;
case EC_IOCTL_MASTER_LINK_STATE:
ret = ec_cdev_ioctl_master_link_state(master, arg, priv);
break;
case EC_IOCTL_APP_TIME: case EC_IOCTL_APP_TIME:
if (!(filp->f_mode & FMODE_WRITE)) { if (!(filp->f_mode & FMODE_WRITE)) {
ret = -EPERM; ret = -EPERM;

View File

@ -88,6 +88,7 @@ static const char *type_strings[] = {
void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram. */) void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram. */)
{ {
INIT_LIST_HEAD(&datagram->queue); // mark as unqueued INIT_LIST_HEAD(&datagram->queue); // mark as unqueued
datagram->device_index = EC_DEVICE_MAIN;
datagram->type = EC_DATAGRAM_NONE; datagram->type = EC_DATAGRAM_NONE;
memset(datagram->address, 0x00, EC_ADDR_LEN); memset(datagram->address, 0x00, EC_ADDR_LEN);
datagram->data = NULL; datagram->data = NULL;
@ -426,13 +427,70 @@ int ec_datagram_brw(
/*****************************************************************************/ /*****************************************************************************/
/** Initializes an EtherCAT LRD datagram. /** 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 * \attention It is assumed, that the external memory is at least \a data_size
* bytes large. * bytes large.
* *
* \return Return value of ec_datagram_prealloc(). * \return Return value of ec_datagram_prealloc().
*/ */
int ec_datagram_lrd( int ec_datagram_lrd_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */ ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */ uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */ 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 * \attention It is assumed, that the external memory is at least \a data_size
* bytes large. * bytes large.
* *
* \return Return value of ec_datagram_prealloc(). * \return Return value of ec_datagram_prealloc().
*/ */
int ec_datagram_lwr( int ec_datagram_lwr_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */ ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */ uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */ 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 * \attention It is assumed, that the external memory is at least \a data_size
* bytes large. * bytes large.
* *
* \return Return value of ec_datagram_prealloc(). * \return Return value of ec_datagram_prealloc().
*/ */
int ec_datagram_lrw( int ec_datagram_lrw_ext(
ec_datagram_t *datagram, /**< EtherCAT datagram. */ ec_datagram_t *datagram, /**< EtherCAT datagram. */
uint32_t offset, /**< Logical address. */ uint32_t offset, /**< Logical address. */
size_t data_size, /**< Number of bytes to read/write. */ size_t data_size, /**< Number of bytes to read/write. */

View File

@ -85,9 +85,10 @@ typedef enum {
/** EtherCAT datagram. /** EtherCAT datagram.
*/ */
typedef struct { typedef struct {
struct list_head list; /**< Needed by domain datagram lists. */
struct list_head queue; /**< Master datagram queue item. */ struct list_head queue; /**< Master datagram queue item. */
struct list_head sent; /**< Master list item for sent datagrams. */ 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.). */ ec_datagram_type_t type; /**< Datagram type (APRD, BWR, etc.). */
uint8_t address[EC_ADDR_LEN]; /**< Recipient address. */ uint8_t address[EC_ADDR_LEN]; /**< Recipient address. */
uint8_t *data; /**< Datagram payload. */ 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_brd(ec_datagram_t *, uint16_t, size_t);
int ec_datagram_bwr(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_brw(ec_datagram_t *, uint16_t, size_t);
int ec_datagram_lrd(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, uint8_t *); int ec_datagram_lwr(ec_datagram_t *, uint32_t, size_t);
int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t, uint8_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_state(const ec_datagram_t *);
void ec_datagram_print_wc_error(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) } while (0)
#endif #endif
/** List of intervals for frame statistics [s].
*/
static const unsigned int rate_intervals[] = {
1, 10, 60
};
/*****************************************************************************/ /*****************************************************************************/
/** Constructor. /** Constructor.
@ -309,30 +303,6 @@ void ec_device_send(
{ {
struct sk_buff *skb = device->tx_skb[device->tx_ring_index]; 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 // set the right length for the data
skb->len = ETH_HLEN + size; skb->len = ETH_HLEN + size;
@ -350,7 +320,9 @@ void ec_device_send(
#endif #endif
{ {
device->tx_count++; device->tx_count++;
device->master->device_stats.tx_count++;
device->tx_bytes += ETH_HLEN + size; device->tx_bytes += ETH_HLEN + size;
device->master->device_stats.tx_bytes += ETH_HLEN + size;
#ifdef EC_DEBUG_IF #ifdef EC_DEBUG_IF
ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size); ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
#endif #endif
@ -375,16 +347,20 @@ void ec_device_clear_stats(
// zero frame statistics // zero frame statistics
device->tx_count = 0; device->tx_count = 0;
device->rx_count = 0;
device->tx_errors = 0;
device->tx_bytes = 0;
device->last_tx_count = 0; device->last_tx_count = 0;
device->rx_count = 0;
device->last_rx_count = 0;
device->tx_bytes = 0;
device->last_tx_bytes = 0; device->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++) { for (i = 0; i < EC_RATE_COUNT; i++) {
device->tx_frame_rates[i] = 0; device->tx_frame_rates[i] = 0;
device->rx_frame_rates[i] = 0;
device->tx_byte_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); 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 * Device interface
*****************************************************************************/ *****************************************************************************/
@ -496,10 +509,21 @@ void ec_device_poll(
void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */) void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
{ {
ec_master_t *master = device->master; 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_mac_print(device->dev->dev_addr, mac_str);
EC_MASTER_INFO(master, "Releasing main device %s.\n", 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); down(&master->device_sem);
ec_device_detach(device); 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 ecdev_open(ec_device_t *device /**< EtherCAT device */)
{ {
int ret; int ret;
ec_master_t *master = device->master;
ret = ec_device_open(device); ret = ec_device_open(device);
if (ret) { if (ret) {
EC_MASTER_ERR(device->master, "Failed to open device!\n"); EC_MASTER_ERR(master, "Failed to open device!\n");
return ret; return ret;
} }
ret = ec_master_enter_idle_phase(device->master); if (master->devices[EC_DEVICE_MAIN].open &&
if (ret) { (ec_mac_is_zero(master->macs[EC_DEVICE_BACKUP]) ||
EC_MASTER_ERR(device->master, "Failed to enter IDLE phase!\n"); master->devices[EC_DEVICE_BACKUP].open)) {
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;
}
} }
return 0; return 0;
@ -541,10 +570,15 @@ int ecdev_open(ec_device_t *device /**< EtherCAT device */)
*/ */
void ecdev_close(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)) if (master->phase == EC_IDLE) {
EC_MASTER_WARN(device->master, "Failed to close device!\n"); 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->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)) { if (unlikely(device->master->debug_level > 1)) {
EC_MASTER_DBG(device->master, 2, "Received frame:\n"); EC_MASTER_DBG(device->master, 2, "Received frame:\n");
@ -603,14 +640,15 @@ void ecdev_set_link(
) )
{ {
if (unlikely(!device)) { 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; return;
} }
if (likely(state != device->link_state)) { if (likely(state != device->link_state)) {
device->link_state = state; device->link_state = state;
EC_MASTER_INFO(device->master, 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)) { 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; return 0;
} }

View File

@ -101,19 +101,24 @@ struct ec_device
u64 tx_count; /**< Number of frames sent. */ u64 tx_count; /**< Number of frames sent. */
u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */ u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
u64 rx_count; /**< Number of frames received. */ 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 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 tx_errors; /**< Number of transmit errors. */
u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */ s32 tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for
unsigned int tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in different statistics cycle periods.
frames/s for different */
statistics cycle periods. */ s32 rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for
unsigned int tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s different statistics cycle periods.
for different statistics */
cycle periods. */ s32 tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s for
int loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different different statistics cycle periods. */
statistics cycle periods. */ s32 rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s for
unsigned long stats_jiffies; /**< Jiffies of last statistic cycle. */ different statistics cycle periods. */
#ifdef EC_DEBUG_IF #ifdef EC_DEBUG_IF
ec_debug_t dbg; /**< debug device */ 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 *); uint8_t *ec_device_tx_data(ec_device_t *);
void ec_device_send(ec_device_t *, size_t); void ec_device_send(ec_device_t *, size_t);
void ec_device_clear_stats(ec_device_t *); void ec_device_clear_stats(ec_device_t *);
void ec_device_update_stats(ec_device_t *);
#ifdef EC_DEBUG_RING #ifdef EC_DEBUG_RING
void ec_device_debug_ring_append(ec_device_t *, ec_debug_frame_dir_t, 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 "slave_config.h"
#include "domain.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 = NULL;
domain->data_origin = EC_ORIG_INTERNAL; domain->data_origin = EC_ORIG_INTERNAL;
domain->logical_base_address = 0x00000000; domain->logical_base_address = 0x00000000;
INIT_LIST_HEAD(&domain->datagrams); INIT_LIST_HEAD(&domain->datagram_pairs);
domain->working_counter = 0x0000; domain->working_counter[EC_DEVICE_MAIN] = 0x0000;
domain->working_counter[EC_DEVICE_BACKUP] = 0x0000;
domain->expected_working_counter = 0x0000; domain->expected_working_counter = 0x0000;
domain->working_counter_changes = 0; domain->working_counter_changes = 0;
domain->redundancy_active = 0;
domain->notify_jiffies = 0; domain->notify_jiffies = 0;
} }
@ -76,12 +81,13 @@ void ec_domain_init(
*/ */
void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */) 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 // dequeue and free datagrams
list_for_each_entry_safe(datagram, next, &domain->datagrams, list) { list_for_each_entry_safe(datagram_pair, next_pair,
ec_datagram_clear(datagram); &domain->datagram_pairs, list) {
kfree(datagram); ec_datagram_pair_clear(datagram_pair);
kfree(datagram_pair);
} }
ec_domain_clear_data(domain); ec_domain_clear_data(domain);
@ -95,8 +101,10 @@ void ec_domain_clear_data(
ec_domain_t *domain /**< EtherCAT domain. */ 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); kfree(domain->data);
}
domain->data = NULL; domain->data = NULL;
domain->data_origin = EC_ORIG_INTERNAL; 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 * The datagrams' types and expected working counters are determined by the
* number of input and output fmmus that share the datagram. * number of input and output fmmus that share the datagrams.
* *
* \retval 0 Success. * \retval 0 Success.
* \retval <0 Error code. * \retval <0 Error code.
*/ */
int ec_domain_add_datagram( int ec_domain_add_datagram_pair(
ec_domain_t *domain, /**< EtherCAT domain. */ ec_domain_t *domain, /**< EtherCAT domain. */
uint32_t logical_offset, /**< Logical offset. */ uint32_t logical_offset, /**< Logical offset. */
size_t data_size, /**< Size of the data. */ size_t data_size, /**< Size of the data. */
uint8_t *data, /**< Process 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; 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, EC_MASTER_ERR(domain->master,
"Failed to allocate domain datagram!\n"); "Failed to allocate domain datagram pair!\n");
return -ENOMEM; return -ENOMEM;
} }
ec_datagram_init(datagram); ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data,
snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE, data_size, used);
"domain%u-%u", domain->index, logical_offset); if (ret) {
kfree(datagram_pair);
if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs return ret;
ret = ec_datagram_lrw(datagram, logical_offset, data_size, data);
if (ret < 0) {
kfree(datagram);
return ret;
}
// If LRW is used, output FMMUs increment the working counter by 2,
// while input FMMUs increment it by 1.
domain->expected_working_counter +=
used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT];
} else if (used[EC_DIR_OUTPUT]) { // outputs only
ret = ec_datagram_lwr(datagram, logical_offset, data_size, data);
if (ret < 0) {
kfree(datagram);
return ret;
}
domain->expected_working_counter += used[EC_DIR_OUTPUT];
} else { // inputs only (or nothing)
ret = ec_datagram_lrd(datagram, logical_offset, data_size, data);
if (ret < 0) {
kfree(datagram);
return ret;
}
domain->expected_working_counter += used[EC_DIR_INPUT];
} }
ec_datagram_zero(datagram); domain->expected_working_counter +=
list_add_tail(&datagram->list, &domain->datagrams); 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; 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. /** Finishes a domain.
* *
* This allocates the necessary datagrams and writes the correct logical * 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_count;
unsigned int datagram_used[EC_DIR_COUNT]; unsigned int datagram_used[EC_DIR_COUNT];
ec_fmmu_config_t *fmmu; ec_fmmu_config_t *fmmu;
ec_fmmu_config_t *fmmu_temp; const ec_fmmu_config_t *datagram_first_fmmu = NULL;
const ec_datagram_t *datagram; const ec_datagram_pair_t *datagram_pair;
int ret; int ret;
domain->logical_base_address = base_address; 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 // - correct the logical base addresses
// - set up the datagrams to carry the process data // - set up the datagrams to carry the process data
// - calculate the datagrams' expected working counters
datagram_offset = 0; datagram_offset = 0;
datagram_size = 0; datagram_size = 0;
datagram_count = 0; datagram_count = 0;
datagram_used[EC_DIR_OUTPUT] = 0; datagram_used[EC_DIR_OUTPUT] = 0;
datagram_used[EC_DIR_INPUT] = 0; datagram_used[EC_DIR_INPUT] = 0;
list_for_each_entry(fmmu_temp, &domain->fmmu_configs, list) { if (!list_empty(&domain->fmmu_configs)) {
// we have to remove the constness, sorry FIXME datagram_first_fmmu =
ec_slave_config_t *sc = (ec_slave_config_t *) fmmu_temp->sc; list_entry(domain->fmmu_configs.next, ec_fmmu_config_t, list);
sc->used_for_fmmu_datagram[fmmu_temp->dir] = 0;
} }
list_for_each_entry(fmmu, &domain->fmmu_configs, list) { list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
// Correct logical FMMU address // Correct logical FMMU address
fmmu->logical_start_address += base_address; fmmu->logical_start_address += base_address;
// Increment Input/Output counter to determine datagram types // Increment Input/Output counter to determine datagram types
// and calculate expected working counters // and calculate expected working counters
if (fmmu->sc->used_for_fmmu_datagram[fmmu->dir] == 0) { if (shall_count(fmmu, datagram_first_fmmu)) {
ec_slave_config_t *sc = (ec_slave_config_t *)fmmu->sc;
datagram_used[fmmu->dir]++; 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, // If the current FMMU's data do not fit in the current datagram,
// allocate a new one. // allocate a new one.
if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) { 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, domain->logical_base_address + datagram_offset,
datagram_size, domain->data + datagram_offset, datagram_size, domain->data + datagram_offset,
datagram_used); datagram_used);
if (ret < 0) if (ret < 0)
return ret; return ret;
datagram_offset += datagram_size; datagram_offset += datagram_size;
datagram_size = 0; datagram_size = 0;
datagram_count++; datagram_count++;
datagram_used[EC_DIR_OUTPUT] = 0; datagram_used[EC_DIR_OUTPUT] = 0;
datagram_used[EC_DIR_INPUT] = 0; datagram_used[EC_DIR_INPUT] = 0;
list_for_each_entry(fmmu_temp, &domain->fmmu_configs, list) { datagram_first_fmmu = fmmu;
ec_slave_config_t *sc = (ec_slave_config_t *)fmmu_temp->sc;
sc->used_for_fmmu_datagram[fmmu_temp->dir] = 0;
}
} }
datagram_size += fmmu->data_size; datagram_size += fmmu->data_size;
} }
// Allocate last datagram, if data are left (this is also the case if the /* Allocate last datagram pair, if data are left (this is also the case if
// process data fit into a single datagram) * the process data fit into a single datagram) */
if (datagram_size) { if (datagram_size) {
ret = ec_domain_add_datagram(domain, ret = ec_domain_add_datagram_pair(domain,
domain->logical_base_address + datagram_offset, domain->logical_base_address + datagram_offset,
datagram_size, domain->data + datagram_offset, datagram_size, domain->data + datagram_offset,
datagram_used); datagram_used);
@ -286,13 +304,16 @@ int ec_domain_finish(
" %zu byte, expected working counter %u.\n", domain->index, " %zu byte, expected working counter %u.\n", domain->index,
domain->logical_base_address, domain->data_size, domain->logical_base_address, domain->data_size,
domain->expected_working_counter); 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," EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x,"
" %zu byte, type %s.\n", datagram->name, " %zu byte, type %s.\n", datagram->name,
EC_READ_U32(datagram->address), datagram->data_size, EC_READ_U32(datagram->address), datagram->data_size,
ec_datagram_type_string(datagram)); ec_datagram_type_string(datagram));
} }
return 0; return 0;
} }
@ -332,8 +353,32 @@ const ec_fmmu_config_t *ec_domain_find_fmmu(
return NULL; 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, 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; const ec_pdo_entry_reg_t *reg;
ec_slave_config_t *sc; ec_slave_config_t *sc;
int ret; int ret;
EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list(" EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list("
"domain = 0x%p, regs = 0x%p)\n", domain, regs); "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) void ecrt_domain_process(ec_domain_t *domain)
{ {
uint16_t working_counter_sum; uint16_t wc_sum[EC_NUM_DEVICES] = {};
ec_datagram_t *datagram; 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; #if DEBUG_REDUNDANCY
list_for_each_entry(datagram, &domain->datagrams, list) { EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index);
ec_datagram_output_stats(datagram); #endif
if (datagram->state == EC_DATAGRAM_RECEIVED) {
working_counter_sum += datagram->working_counter; 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_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 && if (domain->working_counter_changes &&
@ -419,13 +562,19 @@ void ecrt_domain_process(ec_domain_t *domain)
domain->notify_jiffies = jiffies; domain->notify_jiffies = jiffies;
if (domain->working_counter_changes == 1) { if (domain->working_counter_changes == 1) {
EC_MASTER_INFO(domain->master, "Domain %u: Working counter" EC_MASTER_INFO(domain->master, "Domain %u: Working counter"
" changed to %u/%u.\n", domain->index, " changed to %u/%u (%u+%u).\n", domain->index,
domain->working_counter, domain->expected_working_counter); 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 { } else {
EC_MASTER_INFO(domain->master, "Domain %u: %u working counter" EC_MASTER_INFO(domain->master, "Domain %u: %u working counter"
" changes - now %u/%u.\n", domain->index, " changes - now %u/%u (%u+%u).\n", domain->index,
domain->working_counter_changes, domain->working_counter, domain->working_counter_changes,
domain->expected_working_counter); 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; 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) 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) { list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
ec_master_queue_datagram(domain->master, datagram);
/* 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) 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 (state->working_counter) {
if (domain->working_counter == domain->expected_working_counter) { if (state->working_counter == domain->expected_working_counter) {
state->wc_state = EC_WC_COMPLETE; state->wc_state = EC_WC_COMPLETE;
} else { } else {
state->wc_state = EC_WC_INCOMPLETE; 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 { } else {
state->wc_state = EC_WC_ZERO; 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. */ ec_origin_t data_origin; /**< Origin of the \a data memory. */
uint32_t logical_base_address; /**< Logical offset address of the uint32_t logical_base_address; /**< Logical offset address of the
process data. */ 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. */ uint16_t expected_working_counter; /**< Expected working counter. */
unsigned int working_counter_changes; /**< Working counter changes unsigned int working_counter_changes; /**< Working counter changes
since last notification. */ since last notification. */
unsigned int redundancy_active; /**< Non-zero, if redundancy is in use. */
unsigned long notify_jiffies; /**< Time of last notification. */ 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 * Starts a new receiving sequence by queueing a datagram that checks the
* slave's mailbox for a new EoE datagram. * slave's mailbox for a new EoE datagram.
*
* \fixme Use both devices.
*/ */
void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */) void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
{ {
if (eoe->slave->error_flag || 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->rx_idle = 1;
eoe->tx_idle = 1; eoe->tx_idle = 1;
return; 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 * Starts a new transmit sequence. If no data is available, a new receive
* sequence is started instead. * sequence is started instead.
*
* \fixme Use both devices.
*/ */
void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */) 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 #endif
if (eoe->slave->error_flag || 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->rx_idle = 1;
eoe->tx_idle = 1; eoe->tx_idle = 1;
return; 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_sdo_request(ec_fsm_master_t *);
void ec_fsm_master_state_reg_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 *); 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_datagram_t *datagram /**< Datagram object to use. */
) )
{ {
ec_device_index_t dev_idx;
fsm->master = master; fsm->master = master;
fsm->datagram = datagram; fsm->datagram = datagram;
fsm->state = ec_fsm_master_state_start; fsm->state = ec_fsm_master_state_start;
fsm->idle = 0; fsm->idle = 0;
fsm->link_state = 0; fsm->dev_idx = EC_DEVICE_MAIN;
fsm->slaves_responding = 0; 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->rescan_required = 0;
fsm->slave_states = EC_SLAVE_STATE_UNKNOWN;
// init sub-state-machines // init sub-state-machines
ec_fsm_coe_init(&fsm->fsm_coe, fsm->datagram); 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. */ ec_fsm_master_t *fsm /**< Master state machine. */
) )
{ {
fsm->dev_idx = EC_DEVICE_MAIN;
fsm->state = ec_fsm_master_state_start; fsm->state = ec_fsm_master_state_start;
fsm->state(fsm); // execute immediately fsm->state(fsm); // execute immediately
} }
@ -178,6 +185,7 @@ void ec_fsm_master_state_start(
fsm->idle = 1; fsm->idle = 1;
ec_datagram_brd(fsm->datagram, 0x0130, 2); ec_datagram_brd(fsm->datagram, 0x0130, 2);
ec_datagram_zero(fsm->datagram); ec_datagram_zero(fsm->datagram);
fsm->datagram->device_index = fsm->dev_idx;
fsm->state = ec_fsm_master_state_broadcast; fsm->state = ec_fsm_master_state_broadcast;
} }
@ -196,48 +204,58 @@ void ec_fsm_master_state_broadcast(
ec_slave_t *slave; ec_slave_t *slave;
ec_master_t *master = fsm->master; ec_master_t *master = fsm->master;
if (datagram->state == EC_DATAGRAM_TIMED_OUT)
return; // always retry
// bus topology change? // 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->rescan_required = 1;
fsm->slaves_responding = datagram->working_counter; fsm->slaves_responding[fsm->dev_idx] = datagram->working_counter;
EC_MASTER_INFO(master, "%u slave(s) responding.\n", EC_MASTER_INFO(master, "%u slave(s) responding on %s device.\n",
fsm->slaves_responding); fsm->slaves_responding[fsm->dev_idx],
ec_device_names[fsm->dev_idx]);
} }
if (fsm->link_state && !master->main_device.link_state) { if (fsm->link_state[fsm->dev_idx] &&
// link went down !master->devices[fsm->dev_idx].link_state) {
ec_device_index_t dev_idx;
EC_MASTER_DBG(master, 1, "Master state machine detected " 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 #ifdef EC_EOE
ec_master_eoe_stop(master); ec_master_eoe_stop(master);
ec_master_clear_eoe_handlers(master); ec_master_clear_eoe_handlers(master);
#endif #endif
ec_master_clear_slaves(master); 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) { for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
ec_fsm_master_restart(fsm); fsm->slave_states[dev_idx] = 0x00;
return; 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); 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]; char state_str[EC_STATE_STRING_SIZE];
fsm->slave_states = states; fsm->slave_states[fsm->dev_idx] = states;
ec_state_string(fsm->slave_states, state_str, 1); ec_state_string(states, state_str, 1);
EC_MASTER_INFO(master, "Slave states: %s.\n", state_str); EC_MASTER_INFO(master, "Slave states on %s device: %s.\n",
ec_device_names[fsm->dev_idx], state_str);
} }
} else { } 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) { if (fsm->rescan_required) {
@ -245,6 +263,9 @@ void ec_fsm_master_state_broadcast(
if (!master->allow_scan) { if (!master->allow_scan) {
up(&master->scan_sem); up(&master->scan_sem);
} else { } else {
unsigned int count = 0, next_dev_slave, ring_position;
ec_device_index_t dev_idx;
master->scan_busy = 1; master->scan_busy = 1;
up(&master->scan_sem); up(&master->scan_sem);
@ -259,9 +280,12 @@ void ec_fsm_master_state_broadcast(
#endif #endif
ec_master_clear_slaves(master); 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. // no slaves present -> finish state machine.
master->scan_busy = 0; master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue); wake_up_interruptible(&master->scan_queue);
@ -269,12 +293,11 @@ void ec_fsm_master_state_broadcast(
return; return;
} }
size = sizeof(ec_slave_t) * master->slave_count; size = sizeof(ec_slave_t) * count;
if (!(master->slaves = if (!(master->slaves =
(ec_slave_t *) kmalloc(size, GFP_KERNEL))) { (ec_slave_t *) kmalloc(size, GFP_KERNEL))) {
EC_MASTER_ERR(master, "Failed to allocate %u bytes" EC_MASTER_ERR(master, "Failed to allocate %u bytes"
" of slave memory!\n", size); " of slave memory!\n", size);
master->slave_count = 0; // TODO avoid retrying scan!
master->scan_busy = 0; master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue); wake_up_interruptible(&master->scan_queue);
ec_fsm_master_restart(fsm); ec_fsm_master_restart(fsm);
@ -282,21 +305,35 @@ void ec_fsm_master_state_broadcast(
} }
// init slaves // 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; 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 // do not force reconfiguration in operation phase to avoid
// unnecesssary process data interruptions // unnecesssary process data interruptions
if (master->phase != EC_OPERATION) if (master->phase != EC_OPERATION) {
slave->force_config = 1; 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_fsm_master_enter_clear_addresses(fsm);
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;
return; return;
} }
} }
@ -318,6 +355,7 @@ void ec_fsm_master_state_broadcast(
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address, ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
0x0130, 2); 0x0130, 2);
ec_datagram_zero(datagram); ec_datagram_zero(datagram);
fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_read_state; fsm->state = ec_fsm_master_state_read_state;
} }
@ -411,6 +449,7 @@ int ec_fsm_master_action_process_register(
request->offset, request->length); request->offset, request->length);
memcpy(fsm->datagram->data, request->data, request->length); memcpy(fsm->datagram->data, request->data, request->length);
} }
fsm->datagram->device_index = request->slave->device_index;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_reg_request; fsm->state = ec_fsm_master_state_reg_request;
return 1; return 1;
@ -485,8 +524,9 @@ void ec_fsm_master_action_idle(
ec_slave_t *slave; ec_slave_t *slave;
// Check for pending internal SDO requests // Check for pending internal SDO requests
if (ec_fsm_master_action_process_sdo(fsm)) if (ec_fsm_master_action_process_sdo(fsm)) {
return; return;
}
// enable processing of requests // enable processing of requests
for (slave = master->slaves; for (slave = master->slaves;
@ -518,6 +558,7 @@ void ec_fsm_master_action_idle(
fsm->state = ec_fsm_master_state_sdo_dictionary; fsm->state = ec_fsm_master_state_sdo_dictionary;
ec_fsm_coe_dictionary(&fsm->fsm_coe, slave); ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately
fsm->datagram->device_index = fsm->slave->device_index;
return; return;
} }
@ -550,6 +591,7 @@ void ec_fsm_master_action_next_slave_state(
ec_datagram_fprd(fsm->datagram, ec_datagram_fprd(fsm->datagram,
fsm->slave->station_address, 0x0130, 2); fsm->slave->station_address, 0x0130, 2);
ec_datagram_zero(fsm->datagram); ec_datagram_zero(fsm->datagram);
fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_read_state; fsm->state = ec_fsm_master_state_read_state;
return; return;
@ -607,6 +649,7 @@ void ec_fsm_master_action_configure(
fsm->state = ec_fsm_master_state_configure_slave; fsm->state = ec_fsm_master_state_configure_slave;
ec_fsm_slave_config_start(&fsm->fsm_slave_config, slave); ec_fsm_slave_config_start(&fsm->fsm_slave_config, slave);
fsm->state(fsm); // execute immediately fsm->state(fsm); // execute immediately
fsm->datagram->device_index = fsm->slave->device_index;
return; return;
} }
@ -680,8 +723,9 @@ void ec_fsm_master_state_acknowledge(
{ {
ec_slave_t *slave = fsm->slave; ec_slave_t *slave = fsm->slave;
if (ec_fsm_change_exec(&fsm->fsm_change)) if (ec_fsm_change_exec(&fsm->fsm_change)) {
return; return;
}
if (!ec_fsm_change_success(&fsm->fsm_change)) { if (!ec_fsm_change_success(&fsm->fsm_change)) {
fsm->slave->error_flag = 1; 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. /** Master state: CLEAR ADDRESSES.
*/ */
void ec_fsm_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_master_t *master = fsm->master;
ec_datagram_t *datagram = fsm->datagram; 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; return;
}
if (datagram->state != EC_DATAGRAM_RECEIVED) { if (datagram->state != EC_DATAGRAM_RECEIVED) {
EC_MASTER_ERR(master, "Failed to receive address" 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); ec_datagram_print_state(datagram);
master->scan_busy = 0; master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue); wake_up_interruptible(&master->scan_queue);
@ -715,17 +777,20 @@ void ec_fsm_master_state_clear_addresses(
return; return;
} }
if (datagram->working_counter != master->slave_count) { if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
EC_MASTER_WARN(master, "Failed to clear all station addresses:" EC_MASTER_WARN(master, "Failed to clear station addresses on %s link:"
" Cleared %u of %u", " 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" 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_bwr(datagram, 0x0900, 1);
ec_datagram_zero(datagram); ec_datagram_zero(datagram);
fsm->datagram->device_index = fsm->dev_idx;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_measure_delays; fsm->state = ec_fsm_master_state_dc_measure_delays;
} }
@ -745,7 +810,8 @@ void ec_fsm_master_state_dc_measure_delays(
return; return;
if (datagram->state != EC_DATAGRAM_RECEIVED) { 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); ec_datagram_print_state(datagram);
master->scan_busy = 0; master->scan_busy = 0;
wake_up_interruptible(&master->scan_queue); wake_up_interruptible(&master->scan_queue);
@ -753,16 +819,30 @@ void ec_fsm_master_state_dc_measure_delays(
return; return;
} }
EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring.\n", EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring"
datagram->working_counter); " 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"); EC_MASTER_INFO(master, "Scanning bus.\n");
// begin scanning of slaves // begin scanning of slaves
fsm->slave = master->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; fsm->state = ec_fsm_master_state_scan_slave;
ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave); ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately 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 #ifdef EC_EOE
ec_slave_t *slave = fsm->slave; ec_slave_t *slave = fsm->slave;
#endif #endif
if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan))
if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan)) {
return; return;
}
#ifdef EC_EOE #ifdef EC_EOE
if (slave->sii.mailbox_protocols & EC_MBOX_EOE) { if (slave->sii.mailbox_protocols & EC_MBOX_EOE) {
@ -800,8 +882,12 @@ void ec_fsm_master_state_scan_slave(
// another slave to fetch? // another slave to fetch?
fsm->slave++; fsm->slave++;
if (fsm->slave < master->slaves + master->slave_count) { 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_start(&fsm->fsm_slave_scan, fsm->slave);
ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
fsm->datagram->device_index = fsm->slave->device_index;
return; return;
} }
@ -843,8 +929,9 @@ void ec_fsm_master_state_configure_slave(
{ {
ec_master_t *master = fsm->master; 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; return;
}
fsm->slave->force_config = 0; fsm->slave->force_config = 0;
@ -886,6 +973,7 @@ void ec_fsm_master_enter_write_system_times(
// and time offset (0x0920, 64 bit) // and time offset (0x0920, 64 bit)
ec_datagram_fprd(fsm->datagram, fsm->slave->station_address, ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
0x0910, 24); 0x0910, 24);
fsm->datagram->device_index = fsm->slave->device_index;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_read_offset; fsm->state = ec_fsm_master_state_dc_read_offset;
return; return;
@ -1031,6 +1119,7 @@ void ec_fsm_master_state_dc_read_offset(
ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12); ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12);
EC_WRITE_U64(datagram->data, new_offset); EC_WRITE_U64(datagram->data, new_offset);
EC_WRITE_U32(datagram->data + 8, slave->transmission_delay); EC_WRITE_U32(datagram->data + 8, slave->transmission_delay);
fsm->datagram->device_index = slave->device_index;
fsm->retries = EC_FSM_RETRIES; fsm->retries = EC_FSM_RETRIES;
fsm->state = ec_fsm_master_state_dc_write_offset; 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_slave_t *slave = fsm->slave;
ec_master_t *master = fsm->master; 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)) { if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
ec_fsm_master_restart(fsm); ec_fsm_master_restart(fsm);

View File

@ -115,12 +115,19 @@ struct ec_fsm_master {
unsigned int retries; /**< retries on datagram timeout. */ unsigned int retries; /**< retries on datagram timeout. */
void (*state)(ec_fsm_master_t *); /**< master state function */ 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 */ int idle; /**< state machine is in idle phase */
unsigned long scan_jiffies; /**< beginning of slave scanning */ unsigned long scan_jiffies; /**< beginning of slave scanning */
uint8_t link_state; /**< Last main device link state. */ uint8_t link_state[EC_NUM_DEVICES]; /**< Last link state for every device.
unsigned int slaves_responding; /**< number of responding slaves */ */
unsigned int slaves_responding[EC_NUM_DEVICES]; /**< Number of responding
slaves for every device.
*/
unsigned int rescan_required; /**< A bus rescan is required. */ 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_slave_t *slave; /**< current slave */
ec_sii_write_request_t *sii_request; /**< SII write request */ ec_sii_write_request_t *sii_request; /**< SII write request */
off_t sii_index; /**< index to SII write request data */ 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 // Check for pending external SDO requests
if (ec_fsm_slave_action_process_sdo(fsm)) if (ec_fsm_slave_action_process_sdo(fsm)) {
return; return;
}
// Check for pending FoE requests // Check for pending FoE requests
if (ec_fsm_slave_action_process_foe(fsm)) if (ec_fsm_slave_action_process_foe(fsm)) {
return; return;
}
// Check for pending SoE requests // Check for pending SoE requests
if (ec_fsm_slave_action_process_soe(fsm)) if (ec_fsm_slave_action_process_soe(fsm)) {
return; return;
}
} }
/*****************************************************************************/ /*****************************************************************************/

View File

@ -196,6 +196,16 @@ enum {
EC_SDO_ENTRY_ACCESS_COUNT /**< Number of states. */ 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. /** Convenience macro for printing EtherCAT-specific information to syslog.

View File

@ -56,7 +56,7 @@
* *
* Increment this when changing the ioctl interface! * Increment this when changing the ioctl interface!
*/ */
#define EC_IOCTL_VERSION_MAGIC 12 #define EC_IOCTL_VERSION_MAGIC 18
// Command-line tool // Command-line tool
#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t) #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_SEND EC_IO(0x23)
#define EC_IOCTL_RECEIVE EC_IO(0x24) #define EC_IOCTL_RECEIVE EC_IO(0x24)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x25, ec_master_state_t) #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_MASTER_LINK_STATE EC_IOWR(0x26, ec_ioctl_link_state_t)
#define EC_IOCTL_SYNC_REF EC_IO(0x27) #define EC_IOCTL_APP_TIME EC_IOW(0x27, ec_ioctl_app_time_t)
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x28) #define EC_IOCTL_SYNC_REF EC_IO(0x28)
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x29) #define EC_IOCTL_SYNC_SLAVES EC_IO(0x29)
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2a, uint32_t) #define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2a)
#define EC_IOCTL_RESET EC_IO(0x2b) #define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2b, uint32_t)
#define EC_IOCTL_SC_SYNC EC_IOW(0x2c, ec_ioctl_config_t) #define EC_IOCTL_RESET EC_IO(0x2c)
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x2d, ec_ioctl_config_t) #define EC_IOCTL_SC_SYNC 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_WATCHDOG EC_IOW(0x2e, ec_ioctl_config_t)
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x2f, ec_ioctl_config_pdo_t) #define EC_IOCTL_SC_ADD_PDO 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_PDOS EC_IOW(0x20, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x31, ec_ioctl_config_pdo_t) #define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x31, ec_ioctl_add_pdo_entry_t)
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x32, ec_ioctl_reg_pdo_entry_t) #define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x32, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_DC EC_IOW(0x33, ec_ioctl_config_t) #define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x33, ec_ioctl_reg_pdo_entry_t)
#define EC_IOCTL_SC_SDO EC_IOW(0x34, ec_ioctl_sc_sdo_t) #define EC_IOCTL_SC_DC EC_IOW(0x34, ec_ioctl_config_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x35, ec_ioctl_sdo_request_t) #define EC_IOCTL_SC_SDO EC_IOW(0x35, ec_ioctl_sc_sdo_t)
#define EC_IOCTL_SC_VOE EC_IOWR(0x36, ec_ioctl_voe_t) #define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x36, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SC_STATE EC_IOWR(0x37, ec_ioctl_sc_state_t) #define EC_IOCTL_SC_VOE EC_IOWR(0x37, ec_ioctl_voe_t)
#define EC_IOCTL_SC_IDN EC_IOW(0x38, ec_ioctl_sc_idn_t) #define EC_IOCTL_SC_STATE EC_IOWR(0x38, ec_ioctl_sc_state_t)
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x39) #define EC_IOCTL_SC_IDN EC_IOW(0x39, ec_ioctl_sc_idn_t)
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x3a) #define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x3a)
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x3b) #define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x3b)
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x3c, ec_ioctl_domain_state_t) #define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x3c)
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x3d, ec_ioctl_sdo_request_t) #define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x3d, ec_ioctl_domain_state_t)
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x3e, ec_ioctl_sdo_request_t) #define EC_IOCTL_SDO_REQUEST_TIMEOUT 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_STATE 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_READ EC_IOWR(0x30, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x41, ec_ioctl_sdo_request_t) #define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x41, ec_ioctl_sdo_request_t)
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x42, ec_ioctl_voe_t) #define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x42, ec_ioctl_sdo_request_t)
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x43, ec_ioctl_voe_t) #define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x43, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ EC_IOW(0x44, ec_ioctl_voe_t) #define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x44, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x45, ec_ioctl_voe_t) #define EC_IOCTL_VOE_READ EC_IOW(0x45, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x46, ec_ioctl_voe_t) #define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x46, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x47, ec_ioctl_voe_t) #define EC_IOCTL_VOE_WRITE EC_IOWR(0x47, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_DATA EC_IOWR(0x48, ec_ioctl_voe_t) #define EC_IOCTL_VOE_EXEC EC_IOWR(0x48, ec_ioctl_voe_t)
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x49, size_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 tx_count;
uint64_t rx_count; uint64_t rx_count;
uint64_t tx_bytes; uint64_t tx_bytes;
uint64_t rx_bytes;
uint64_t tx_errors; uint64_t tx_errors;
uint32_t tx_frame_rates[EC_RATE_COUNT]; int32_t tx_frame_rates[EC_RATE_COUNT];
uint32_t tx_byte_rates[EC_RATE_COUNT]; int32_t rx_frame_rates[EC_RATE_COUNT];
int32_t loss_rates[EC_RATE_COUNT]; int32_t tx_byte_rates[EC_RATE_COUNT];
} devices[2]; 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; uint64_t app_time;
uint16_t ref_clock; uint16_t ref_clock;
} ec_ioctl_master_t; } ec_ioctl_master_t;
@ -184,6 +196,7 @@ typedef struct {
uint16_t position; uint16_t position;
// outputs // outputs
unsigned int device_index;
uint32_t vendor_id; uint32_t vendor_id;
uint32_t product_code; uint32_t product_code;
uint32_t revision_number; uint32_t revision_number;
@ -279,7 +292,7 @@ typedef struct {
// outputs // outputs
uint32_t data_size; uint32_t data_size;
uint32_t logical_base_address; uint32_t logical_base_address;
uint16_t working_counter; uint16_t working_counter[EC_NUM_DEVICES];
uint16_t expected_working_counter; uint16_t expected_working_counter;
uint32_t fmmu_count; uint32_t fmmu_count;
} ec_ioctl_domain_t; } 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 { typedef struct {
// inputs // inputs
uint64_t app_time; uint64_t app_time;

View File

@ -82,6 +82,12 @@ static unsigned long ext_injection_timeout_jiffies;
#endif #endif
/** List of intervals for statistics [s].
*/
const unsigned int rate_intervals[] = {
1, 10, 60
};
/*****************************************************************************/ /*****************************************************************************/
void ec_master_clear_slave_configs(ec_master_t *); 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 *); static int ec_master_eoe_thread(void *);
#endif #endif
void ec_master_find_dc_ref_clock(ec_master_t *); 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 #ifdef EC_HAVE_CYCLES
timeout_cycles = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); 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 #else
// one jiffy may always elapse between time measurement // one jiffy may always elapse between time measurement
timeout_jiffies = max(EC_IO_TIMEOUT * HZ / 1000000, 1); 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 #endif
} }
@ -132,8 +142,9 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
sema_init(&master->master_sem, 1); sema_init(&master->master_sem, 1);
master->main_mac = main_mac; master->macs[EC_DEVICE_MAIN] = main_mac;
master->backup_mac = backup_mac; master->macs[EC_DEVICE_BACKUP] = backup_mac;
ec_master_clear_device_stats(master);
sema_init(&master->device_sem, 1); 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_waitqueue_head(&master->reg_queue);
// init devices // init devices
ret = ec_device_init(&master->main_device, master); ret = ec_device_init(&master->devices[EC_DEVICE_MAIN], master);
if (ret < 0) if (ret < 0)
goto out_return; goto out_return;
ret = ec_device_init(&master->backup_device, master); ret = ec_device_init(&master->devices[EC_DEVICE_BACKUP], master);
if (ret < 0) if (ret < 0)
goto out_clear_main; goto out_clear_main;
@ -224,7 +235,8 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
// init reference sync datagram // init reference sync datagram
ec_datagram_init(&master->ref_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); ret = ec_datagram_apwr(&master->ref_sync_datagram, 0, 0x0910, 8);
if (ret < 0) { if (ret < 0) {
ec_datagram_clear(&master->ref_sync_datagram); 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 // init sync monitor datagram
ec_datagram_init(&master->sync_mon_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); ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
if (ret < 0) { if (ret < 0) {
ec_datagram_clear(&master->sync_mon_datagram); ec_datagram_clear(&master->sync_mon_datagram);
@ -299,9 +312,9 @@ out_clear_fsm:
ec_fsm_master_clear(&master->fsm); ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram); ec_datagram_clear(&master->fsm_datagram);
out_clear_backup: out_clear_backup:
ec_device_clear(&master->backup_device); ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
out_clear_main: out_clear_main:
ec_device_clear(&master->main_device); ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
out_return: out_return:
return ret; return ret;
} }
@ -334,8 +347,8 @@ void ec_master_clear(
ec_datagram_clear(&master->ref_sync_datagram); ec_datagram_clear(&master->ref_sync_datagram);
ec_fsm_master_clear(&master->fsm); ec_fsm_master_clear(&master->fsm);
ec_datagram_clear(&master->fsm_datagram); ec_datagram_clear(&master->fsm_datagram);
ec_device_clear(&master->backup_device); ec_device_clear(&master->devices[EC_DEVICE_BACKUP]);
ec_device_clear(&master->main_device); ec_device_clear(&master->devices[EC_DEVICE_MAIN]);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -541,6 +554,7 @@ int ec_master_enter_idle_phase(
) )
{ {
int ret; int ret;
ec_device_index_t dev_idx;
EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n"); EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n");
@ -551,7 +565,9 @@ int ec_master_enter_idle_phase(
master->phase = EC_IDLE; master->phase = EC_IDLE;
// reset number of responding slaves to trigger scanning // 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, ret = ec_master_thread_start(master, ec_master_idle_thread,
"EtherCAT-IDLE"); "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. /** 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; int ret = 0;
ec_slave_t *slave; ec_slave_t *slave;
@ -622,7 +640,8 @@ int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
up(&master->scan_sem); up(&master->scan_sem);
// wait for slave scan to complete // 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) { if (ret) {
EC_MASTER_INFO(master, "Waiting for slave scan" EC_MASTER_INFO(master, "Waiting for slave scan"
" interrupted by signal.\n"); " interrupted by signal.\n");
@ -712,8 +731,7 @@ void ec_master_inject_external_datagrams(
#endif #endif
datagram->jiffies_sent = 0; datagram->jiffies_sent = 0;
ec_master_queue_datagram(master, datagram); ec_master_queue_datagram(master, datagram);
} } else {
else {
if (datagram->data_size > master->max_queue_size) { if (datagram->data_size > master->max_queue_size) {
list_del_init(&datagram->queue); list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_ERROR; 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; ec_datagram_t *datagram, *next;
size_t datagram_size; size_t datagram_size;
uint8_t *frame_data, *cur_data; uint8_t *frame_data, *cur_data = NULL;
void *follows_word; void *follows_word;
#ifdef EC_HAVE_CYCLES #ifdef EC_HAVE_CYCLES
cycles_t cycles_start, cycles_sent, cycles_end; 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; frame_count = 0;
INIT_LIST_HEAD(&sent_datagrams); 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 { do {
// fetch pointer to transmit socket buffer frame_data = NULL;
frame_data = ec_device_tx_data(&master->main_device);
cur_data = frame_data + EC_FRAME_HEADER_SIZE;
follows_word = NULL; follows_word = NULL;
more_datagrams_waiting = 0; more_datagrams_waiting = 0;
// fill current frame with datagrams // fill current frame with datagrams
list_for_each_entry(datagram, &master->datagram_queue, queue) { 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? // does the current datagram fit in the frame?
datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size 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); list_add_tail(&datagram->sent, &sent_datagrams);
datagram->index = master->datagram_index++; 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); datagram->index);
// set "datagram following" flag in previous frame // set "datagram following" flag in previous datagram
if (follows_word) if (follows_word) {
EC_WRITE_U16(follows_word, EC_READ_U16(follows_word) | 0x8000); EC_WRITE_U16(follows_word,
EC_READ_U16(follows_word) | 0x8000);
}
// EtherCAT datagram header // 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); EC_WRITE_U8 (cur_data + 1, datagram->index);
memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN); memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN);
EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF); 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 // EtherCAT frame header
EC_WRITE_U16(frame_data, ((cur_data - frame_data EC_WRITE_U16(frame_data, ((cur_data - frame_data
- EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000); - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
// pad frame // pad frame
while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN) 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); EC_MASTER_DBG(master, 2, "frame size: %zu\n", cur_data - frame_data);
// send frame // 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 #ifdef EC_HAVE_CYCLES
cycles_sent = get_cycles(); cycles_sent = get_cycles();
#endif #endif
@ -990,8 +1023,8 @@ void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
#ifdef EC_HAVE_CYCLES #ifdef EC_HAVE_CYCLES
if (unlikely(master->debug_level > 1)) { if (unlikely(master->debug_level > 1)) {
cycles_end = get_cycles(); cycles_end = get_cycles();
EC_MASTER_DBG(master, 0, "ec_master_send_datagrams" EC_MASTER_DBG(master, 0, "%s()"
" sent %u frames in %uus.\n", frame_count, " sent %u frames in %uus.\n", __func__, frame_count,
(unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz); (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
} }
#endif #endif
@ -1090,7 +1123,7 @@ void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
EC_DATAGRAM_HEADER_SIZE + data_size EC_DATAGRAM_HEADER_SIZE + data_size
+ EC_DATAGRAM_FOOTER_SIZE); + EC_DATAGRAM_FOOTER_SIZE);
#ifdef EC_DEBUG_RING #ifdef EC_DEBUG_RING
ec_device_debug_ring_print(&master->main_device); ec_device_debug_ring_print(&master->devices[EC_DEVICE_MAIN]);
#endif #endif
} }
@ -1115,9 +1148,11 @@ void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
// dequeue the received datagram // dequeue the received datagram
datagram->state = EC_DATAGRAM_RECEIVED; datagram->state = EC_DATAGRAM_RECEIVED;
#ifdef EC_HAVE_CYCLES #ifdef EC_HAVE_CYCLES
datagram->cycles_received = master->main_device.cycles_poll; datagram->cycles_received =
master->devices[EC_DEVICE_MAIN].cycles_poll;
#endif #endif
datagram->jiffies_received = master->main_device.jiffies_poll; datagram->jiffies_received =
master->devices[EC_DEVICE_MAIN].jiffies_poll;
list_del_init(&datagram->queue); 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); ecrt_master_send(master);
#ifdef EC_USE_HRTIMER #ifdef EC_USE_HRTIMER
sent_bytes = master->main_device.tx_skb[ sent_bytes = master->devices[EC_DEVICE_MAIN].tx_skb[
master->main_device.tx_ring_index]->len; master->devices[EC_DEVICE_MAIN].tx_ring_index]->len;
#endif #endif
up(&master->io_sem); 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", EC_MASTER_DBG(master, 1, "ecrt_master_create_domain(master = 0x%p)\n",
master); 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"); EC_MASTER_ERR(master, "Error allocating domain memory!\n");
return ERR_PTR(-ENOMEM); return ERR_PTR(-ENOMEM);
} }
@ -2115,32 +2228,42 @@ void ecrt_master_deactivate(ec_master_t *master)
void ecrt_master_send(ec_master_t *master) void ecrt_master_send(ec_master_t *master)
{ {
ec_datagram_t *datagram, *n; ec_datagram_t *datagram, *n;
ec_device_index_t dev_idx;
if (master->injection_seq_rt != master->injection_seq_fsm) { 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); ec_master_queue_datagram(master, &master->fsm_datagram);
master->injection_seq_rt = master->injection_seq_fsm; master->injection_seq_rt = master->injection_seq_fsm;
} }
ec_master_inject_external_datagrams(master); ec_master_inject_external_datagrams(master);
if (unlikely(!master->main_device.link_state)) { for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_NUM_DEVICES; dev_idx++) {
// link is down, no datagram can be sent if (unlikely(!master->devices[dev_idx].link_state)) {
list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) { // link is down, no datagram can be sent
datagram->state = EC_DATAGRAM_ERROR; list_for_each_entry_safe(datagram, n,
list_del_init(&datagram->queue); &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 // send frames
ec_device_poll(&master->main_device); ec_master_send_datagrams(master, dev_idx);
// clear frame statistics
ec_device_clear_stats(&master->main_device);
return;
} }
// send frames
ec_master_send_datagrams(master);
} }
/*****************************************************************************/ /*****************************************************************************/
@ -2150,18 +2273,22 @@ void ecrt_master_receive(ec_master_t *master)
ec_datagram_t *datagram, *next; ec_datagram_t *datagram, *next;
// receive datagrams // 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 // dequeue all datagrams that timed out
list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) { list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
if (datagram->state != EC_DATAGRAM_SENT) continue; if (datagram->state != EC_DATAGRAM_SENT) continue;
#ifdef EC_HAVE_CYCLES #ifdef EC_HAVE_CYCLES
if (master->main_device.cycles_poll - datagram->cycles_sent if (master->devices[EC_DEVICE_MAIN].cycles_poll -
> timeout_cycles) { datagram->cycles_sent > timeout_cycles) {
#else #else
if (master->main_device.jiffies_poll - datagram->jiffies_sent if (master->devices[EC_DEVICE_MAIN].jiffies_poll -
> timeout_jiffies) { datagram->jiffies_sent > timeout_jiffies) {
#endif #endif
list_del_init(&datagram->queue); list_del_init(&datagram->queue);
datagram->state = EC_DATAGRAM_TIMED_OUT; datagram->state = EC_DATAGRAM_TIMED_OUT;
@ -2171,10 +2298,12 @@ void ecrt_master_receive(ec_master_t *master)
if (unlikely(master->debug_level > 0)) { if (unlikely(master->debug_level > 0)) {
unsigned int time_us; unsigned int time_us;
#ifdef EC_HAVE_CYCLES #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; datagram->cycles_sent) * 1000 / cpu_khz;
#else #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); datagram->jiffies_sent) * 1000000 / HZ);
#endif #endif
EC_MASTER_DBG(master, 0, "TIMED OUT datagram %p," 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 = 0x%p)\n", master, master_info);
master_info->slave_count = master->slave_count; 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->scan_busy = master->scan_busy;
master_info->app_time = master->app_time; master_info->app_time = master->app_time;
return 0; 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) void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
{ {
state->slaves_responding = master->fsm.slaves_responding; ec_device_index_t dev_idx;
state->al_states = master->fsm.slave_states;
state->link_up = master->main_device.link_state; 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_get_slave);
EXPORT_SYMBOL(ecrt_master_slave_config); EXPORT_SYMBOL(ecrt_master_slave_config);
EXPORT_SYMBOL(ecrt_master_state); EXPORT_SYMBOL(ecrt_master_state);
EXPORT_SYMBOL(ecrt_master_link_state);
EXPORT_SYMBOL(ecrt_master_application_time); EXPORT_SYMBOL(ecrt_master_application_time);
EXPORT_SYMBOL(ecrt_master_sync_reference_clock); EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
EXPORT_SYMBOL(ecrt_master_sync_slave_clocks); 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. /** EtherCAT master.
* *
* Manages slaves, domains and IO. * Manages slaves, domains and IO.
@ -154,11 +185,10 @@ struct ec_master {
struct semaphore master_sem; /**< Master semaphore. */ struct semaphore master_sem; /**< Master semaphore. */
ec_device_t main_device; /**< EtherCAT main device. */ ec_device_t devices[EC_NUM_DEVICES]; /**< EtherCAT devices. */
const uint8_t *main_mac; /**< MAC address of main device. */ const uint8_t *macs[EC_NUM_DEVICES]; /**< Device MAC addresses. */
ec_device_t backup_device; /**< EtherCAT backup device. */
const uint8_t *backup_mac; /**< MAC address of backup device. */
struct semaphore device_sem; /**< Device semaphore. */ struct semaphore device_sem; /**< Device semaphore. */
ec_device_stats_t device_stats; /**< Device statistics. */
ec_fsm_master_t fsm; /**< Master state machine. */ ec_fsm_master_t fsm; /**< Master state machine. */
ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */ ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
@ -210,8 +240,8 @@ struct ec_master {
ext_datagram_queue. */ ext_datagram_queue. */
struct list_head external_datagram_queue; /**< External Datagram queue. */ struct list_head external_datagram_queue; /**< External Datagram queue. */
unsigned int send_interval; /**< Interval between calls to unsigned int send_interval; /**< Interval between two calls to
ecrt_master_send */ ecrt_master_send(). */
size_t max_queue_size; /**< Maximum size of datagram queue */ size_t max_queue_size; /**< Maximum size of datagram queue */
unsigned int debug_level; /**< Master debug level. */ 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_send_cb(void *);
void ec_master_internal_receive_cb(void *); void ec_master_internal_receive_cb(void *);
extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c
/*****************************************************************************/ /*****************************************************************************/
#endif #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. /** Print a MAC address to a buffer.
*
* The buffer size must be at least EC_MAX_MAC_STRING_SIZE.
* *
* \return number of bytes written. * \return number of bytes written.
*/ */
ssize_t ec_mac_print( ssize_t ec_mac_print(
const uint8_t *mac, /**< MAC address */ const uint8_t *mac, /**< MAC address */
char *buffer /**< target buffer */ char *buffer /**< Target buffer. */
) )
{ {
off_t off = 0; off_t off = 0;
@ -289,7 +295,7 @@ int ec_mac_is_broadcast(
/** Parse a MAC address from a string. /** 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}". * "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}".
* *
* \return 0 on success, else < 0 * \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 (!strlen(src)) {
if (allow_empty){ if (allow_empty){
return 0; return 0;
} } else {
else {
EC_ERR("MAC address may not be empty.\n"); EC_ERR("MAC address may not be empty.\n");
return -EINVAL; return -EINVAL;
} }
@ -319,8 +324,9 @@ static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty)
return -EINVAL; return -EINVAL;
} }
mac[i] = value; mac[i] = value;
if (i < ETH_ALEN - 1) if (i < ETH_ALEN - 1) {
src = rem + 1; // skip colon src = rem + 1; // skip colon
}
} }
return 0; return 0;
@ -445,6 +451,13 @@ size_t ec_state_string(uint8_t states, /**< slave states */
* Device interface * Device interface
*****************************************************************************/ *****************************************************************************/
/** Device names.
*/
const char *ec_device_names[EC_NUM_DEVICES] = {
"main",
"backup"
};
/** Offers an EtherCAT device to a certain master. /** Offers an EtherCAT device to a certain master.
* *
* The master decides, if it wants to use the device for EtherCAT operation, * 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; ec_master_t *master;
char str[20]; char str[EC_MAX_MAC_STRING_SIZE];
unsigned int i; unsigned int i, j;
for (i = 0; i < master_count; i++) { for (i = 0; i < master_count; i++) {
master = &masters[i]; master = &masters[i];
ec_mac_print(net_dev->dev_addr, str);
down(&master->device_sem); 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) for (j = 0; j < EC_NUM_DEVICES; j++) {
|| ec_mac_is_broadcast(master->main_mac)) { if (!master->devices[j].dev
ec_mac_print(net_dev->dev_addr, str); && (ec_mac_equal(master->macs[j], net_dev->dev_addr)
EC_INFO("Accepting device %s for master %u.\n", || ec_mac_is_broadcast(master->macs[j]))) {
str, master->index);
ec_device_attach(&master->main_device, net_dev, poll, module); EC_INFO("Accepting %s as %s device for master %u.\n",
up(&master->device_sem); 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 snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
} ec_device_names[j][0], master->index);
else {
up(&master->device_sem);
if (master->debug_level) { return &master->devices[j]; // offer accepted
ec_mac_print(net_dev->dev_addr, str);
EC_MASTER_DBG(master, 0, "Master declined device %s.\n",
str);
} }
} }
up(&master->device_sem);
EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
} }
return NULL; // offer declined return NULL; // offer declined
} }
/****************************************************************************** /******************************************************************************
* Realtime interface * Application interface
*****************************************************************************/ *****************************************************************************/
/** Request a master. /** Request a master.
@ -514,6 +523,7 @@ ec_master_t *ecrt_request_master_err(
) )
{ {
ec_master_t *master, *errptr = NULL; ec_master_t *master, *errptr = NULL;
unsigned int i, got_modules = 0;
EC_INFO("Requesting master %u...\n", master_index); EC_INFO("Requesting master %u...\n", master_index);
@ -550,11 +560,17 @@ ec_master_t *ecrt_request_master_err(
goto out_release; goto out_release;
} }
if (!try_module_get(master->main_device.module)) { for (i = 0; i < EC_NUM_DEVICES; i++) {
up(&master->device_sem); ec_device_t *device = &master->devices[i];
EC_ERR("Device module is unloading!\n"); if (device->dev) {
errptr = ERR_PTR(-ENODEV); if (!try_module_get(device->module)) {
goto out_release; 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); up(&master->device_sem);
@ -569,7 +585,12 @@ ec_master_t *ecrt_request_master_err(
return master; return master;
out_module_put: 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: out_release:
master->reserved = 0; master->reserved = 0;
out_return: out_return:
@ -588,6 +609,8 @@ ec_master_t *ecrt_request_master(unsigned int master_index)
void ecrt_release_master(ec_master_t *master) void ecrt_release_master(ec_master_t *master)
{ {
unsigned int i;
EC_MASTER_INFO(master, "Releasing master...\n"); EC_MASTER_INFO(master, "Releasing master...\n");
if (!master->reserved) { if (!master->reserved) {
@ -598,7 +621,12 @@ void ecrt_release_master(ec_master_t *master)
ec_master_leave_operation_phase(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; master->reserved = 0;
EC_MASTER_INFO(master, "Released.\n"); 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( void ec_slave_init(
ec_slave_t *slave, /**< EtherCAT slave */ ec_slave_t *slave, /**< EtherCAT slave */
ec_master_t *master, /**< EtherCAT master */ ec_master_t *master, /**< EtherCAT master */
ec_device_index_t dev_idx, /**< Device index. */
uint16_t ring_position, /**< ring position */ uint16_t ring_position, /**< ring position */
uint16_t station_address /**< station address to configure */ uint16_t station_address /**< station address to configure */
) )
@ -70,6 +71,7 @@ void ec_slave_init(
int ret; int ret;
slave->master = master; slave->master = master;
slave->device_index = dev_idx;
slave->ring_position = ring_position; slave->ring_position = ring_position;
slave->station_address = station_address; slave->station_address = station_address;
slave->effective_alias = 0x0000; slave->effective_alias = 0x0000;

View File

@ -174,6 +174,8 @@ typedef struct {
struct ec_slave struct ec_slave
{ {
ec_master_t *master; /**< Master owning the slave. */ ec_master_t *master; /**< Master owning the slave. */
ec_device_index_t device_index; /**< Index of device the slave responds
on. */
// addresses // addresses
uint16_t ring_position; /**< Ring position. */ uint16_t ring_position; /**< Ring position. */
@ -238,7 +240,8 @@ struct ec_slave
/*****************************************************************************/ /*****************************************************************************/
// slave construction/destruction // 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(ec_slave_t *);
void ec_slave_clear_sync_managers(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]); ec_sync_config_init(&sc->sync_configs[i]);
sc->used_fmmus = 0; 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_assign_activate = 0x0000;
sc->dc_sync[0].cycle_time = 0x00000000; sc->dc_sync[0].cycle_time = 0x00000000;
sc->dc_sync[1].cycle_time = 0x00000000; sc->dc_sync[1].cycle_time = 0x00000000;

View File

@ -134,9 +134,6 @@ struct ec_slave_config {
configurations. */ configurations. */
ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */ ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */
uint8_t used_fmmus; /**< Number of FMMUs used. */ 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. */ uint16_t dc_assign_activate; /**< Vendor-specific AssignActivate word. */
ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT]; /**< DC sync signals. */ 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? if (voe->config->slave) { // FIXME locking?
voe->state(voe); 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); ec_master_queue_datagram(voe->config->master, &voe->datagram);
}
} else { } else {
voe->state = ec_voe_handler_state_error; voe->state = ec_voe_handler_state_error;
voe->request_state = EC_INT_REQUEST_FAILURE; voe->request_state = EC_INT_REQUEST_FAILURE;

View File

@ -4,10 +4,12 @@
# #
# $Id$ # $Id$
# #
# vim: spelllang=en spell tw=78
#
#------------------------------------------------------------------------------ #------------------------------------------------------------------------------
# #
# Master devices. # Main Ethernet devices.
# #
# The MASTER<X>_DEVICE variable specifies the Ethernet device for a master # The MASTER<X>_DEVICE variable specifies the Ethernet device for a master
# with index 'X'. # with index 'X'.
@ -25,6 +27,15 @@
MASTER0_DEVICE="" MASTER0_DEVICE=""
#MASTER1_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. # Ethernet driver modules to use for EtherCAT operation.
# #

View File

@ -136,6 +136,11 @@ void CommandDomains::showDomain(
ec_ioctl_domain_fmmu_t fmmu; ec_ioctl_domain_fmmu_t fmmu;
unsigned int dataOffset; unsigned int dataOffset;
string indent(doIndent ? " " : ""); 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 << ":" cout << indent << "Domain" << dec << domain.index << ":"
<< " LogBaseAddr 0x" << " LogBaseAddr 0x"
@ -144,8 +149,14 @@ void CommandDomains::showDomain(
<< ", Size " << dec << setfill(' ') << ", Size " << dec << setfill(' ')
<< setw(3) << domain.data_size << setw(3) << domain.data_size
<< ", WorkingCounter " << ", WorkingCounter "
<< domain.working_counter << "/" << wc_sum << "/"
<< domain.expected_working_counter << endl; << 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) if (!domain.data_size || getVerbosity() != Verbose)
return; return;

View File

@ -93,7 +93,7 @@ void CommandMaster::execute(const StringVector &args)
<< " Phase: "; << " Phase: ";
switch (data.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 1: cout << "Idle"; break;
case 2: cout << "Operation"; break; case 2: cout << "Operation"; break;
default: cout << "???"; default: cout << "???";
@ -114,12 +114,6 @@ void CommandMaster::execute(const StringVector &args)
&& data.devices[i].address[5] == 0x00) { && data.devices[i].address[5] == 0x00) {
cout << "None."; cout << "None.";
} else { } 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') cout << hex << setfill('0')
<< setw(2) << (unsigned int) data.devices[i].address[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 << (data.devices[i].link_state ? "UP" : "DOWN") << endl
<< " Tx frames: " << " Tx frames: "
<< data.devices[i].tx_count << endl << data.devices[i].tx_count << endl
<< " Rx frames: "
<< data.devices[i].rx_count << endl
<< " Lost frames: " << lost << endl
<< " Tx bytes: " << " Tx bytes: "
<< data.devices[i].tx_bytes << endl << data.devices[i].tx_bytes << endl
<< " Rx frames: "
<< data.devices[i].rx_count << endl
<< " Rx bytes: "
<< data.devices[i].rx_bytes << endl
<< " Tx errors: " << " Tx errors: "
<< data.devices[i].tx_errors << endl << data.devices[i].tx_errors << endl
<< " Tx frame rate [1/s]: " << " Tx frame rate [1/s]: "
@ -166,33 +161,106 @@ void CommandMaster::execute(const StringVector &args)
} }
} }
cout << endl cout << endl
<< " Loss rate [1/s]: " << " Rx frame rate [1/s]: "
<< setprecision(0) << fixed; << setfill(' ') << setprecision(0) << fixed;
for (j = 0; j < EC_RATE_COUNT; j++) { for (j = 0; j < EC_RATE_COUNT; j++) {
cout << setw(ColWidth) 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) { if (j < EC_RATE_COUNT - 1) {
cout << " "; cout << " ";
} }
} }
cout << endl cout << endl
<< " Frame loss [%]: " << " Rx rate [KByte/s]: "
<< setprecision(1) << fixed; << setprecision(1) << fixed;
for (j = 0; j < EC_RATE_COUNT; j++) { for (j = 0; j < EC_RATE_COUNT; j++) {
double perc = 0.0; cout << setw(ColWidth)
if (data.devices[i].tx_frame_rates[j]) { << data.devices[i].rx_byte_rates[j] / 1024.0;
perc = 100.0 * data.devices[i].loss_rates[j] /
data.devices[i].tx_frame_rates[j];
}
cout << setw(ColWidth) << perc;
if (j < EC_RATE_COUNT - 1) { if (j < EC_RATE_COUNT - 1) {
cout << " "; cout << " ";
} }
} }
cout << setprecision(0) << endl; 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 cout << " Distributed clocks:" << endl
<< " Reference clock: "; << " Reference clock: ";

View File

@ -144,7 +144,7 @@ void CommandSlaves::listSlaves(
) )
{ {
ec_ioctl_master_t master; ec_ioctl_master_t master;
unsigned int i; unsigned int i, lastDevice;
ec_ioctl_slave_t slave; ec_ioctl_slave_t slave;
uint16_t lastAlias, aliasIndex; uint16_t lastAlias, aliasIndex;
Info info; Info info;
@ -184,6 +184,7 @@ void CommandSlaves::listSlaves(
info.state = alStateString(slave.al_state); info.state = alStateString(slave.al_state);
info.flag = (slave.error_flag ? 'E' : '+'); info.flag = (slave.error_flag ? 'E' : '+');
info.device = slave.device_index;
if (strlen(slave.name)) { if (strlen(slave.name)) {
info.name = slave.name; info.name = slave.name;
@ -215,7 +216,12 @@ void CommandSlaves::listSlaves(
cout << "Master" << dec << m.getIndex() << endl; cout << "Master" << dec << m.getIndex() << endl;
} }
lastDevice = EC_DEVICE_MAIN;
for (iter = infoList.begin(); iter != infoList.end(); iter++) { 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 cout << indent << setfill(' ') << right
<< setw(maxPosWidth) << iter->pos << " " << setw(maxPosWidth) << iter->pos << " "
<< setw(maxAliasWidth) << iter->alias << setw(maxAliasWidth) << iter->alias
@ -245,6 +251,7 @@ void CommandSlaves::showSlaves(
cout << "Alias: " << si->alias << endl; cout << "Alias: " << si->alias << endl;
cout cout
<< "Device: " << (si->device_index ? "Backup" : "Main") << endl
<< "State: " << alStateString(si->al_state) << endl << "State: " << alStateString(si->al_state) << endl
<< "Flag: " << (si->error_flag ? 'E' : '+') << endl << "Flag: " << (si->error_flag ? 'E' : '+') << endl
<< "Identity:" << endl << "Identity:" << endl
@ -332,7 +339,8 @@ void CommandSlaves::showSlaves(
} }
cout << " " << setw(10); cout << " " << setw(10);
if (!si->ports[i].link.loop_closed) { 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 { } else {
cout << "-"; cout << "-";
} }
@ -406,7 +414,8 @@ void CommandSlaves::showSlaves(
<< " Enable SDO: " << " Enable SDO: "
<< (si->coe_details.enable_sdo ? "yes" : "no") << endl << (si->coe_details.enable_sdo ? "yes" : "no") << endl
<< " Enable SDO Info: " << " Enable SDO Info: "
<< (si->coe_details.enable_sdo_info ? "yes" : "no") << endl << (si->coe_details.enable_sdo_info ? "yes" : "no")
<< endl
<< " Enable PDO Assign: " << " Enable PDO Assign: "
<< (si->coe_details.enable_pdo_assign << (si->coe_details.enable_pdo_assign
? "yes" : "no") << endl ? "yes" : "no") << endl

View File

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