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