--- a/master/domain.c Thu Sep 06 14:21:02 2012 +0200
+++ b/master/domain.c Mon Nov 03 15:20:05 2014 +0100
@@ -41,6 +41,11 @@
#include "slave_config.h"
#include "domain.h"
+#include "datagram_pair.h"
+
+/** Extra debug output for redundancy functions.
+ */
+#define DEBUG_REDUNDANCY 0
/*****************************************************************************/
@@ -56,18 +61,23 @@
unsigned int index /**< Index. */
)
{
+ unsigned int dev_idx;
+
domain->master = master;
domain->index = index;
INIT_LIST_HEAD(&domain->fmmu_configs);
domain->data_size = 0;
- domain->tx_size = 0;
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);
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ domain->working_counter[dev_idx] = 0x0000;
+ }
domain->expected_working_counter = 0x0000;
domain->working_counter_changes = 0;
+ domain->redundancy_active = 0;
domain->notify_jiffies = 0;
}
@@ -77,12 +87,13 @@
*/
void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */)
{
- ec_datagram_t *datagram, *next;
+ ec_datagram_pair_t *datagram_pair, *next_pair;
// dequeue and free datagrams
- list_for_each_entry_safe(datagram, next, &domain->datagrams, list) {
- ec_datagram_clear(datagram);
- kfree(datagram);
+ list_for_each_entry_safe(datagram_pair, next_pair,
+ &domain->datagram_pairs, list) {
+ ec_datagram_pair_clear(datagram_pair);
+ kfree(datagram_pair);
}
ec_domain_clear_data(domain);
@@ -99,6 +110,7 @@
if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) {
kfree(domain->data);
}
+
domain->data = NULL;
domain->data_origin = EC_ORIG_INTERNAL;
}
@@ -115,7 +127,6 @@
fmmu->domain = domain;
domain->data_size += fmmu->data_size;
- domain->tx_size += fmmu->tx_size;
list_add_tail(&fmmu->list, &domain->fmmu_configs);
EC_MASTER_DBG(domain->master, 1, "Domain %u:"
@@ -125,15 +136,15 @@
/*****************************************************************************/
-/** Allocates a domain datagram and appends it to the list.
- *
- * The datagram type and expected working counters are determined by the
- * number of input and output fmmus that share the datagram.
+/** Allocates a domain datagram pair and appends it to the list.
+ *
+ * The datagrams' types and expected working counters are determined by the
+ * number of input and output fmmus that share the datagrams.
*
* \retval 0 Success.
* \retval <0 Error code.
*/
-int ec_domain_add_datagram(
+int ec_domain_add_datagram_pair(
ec_domain_t *domain, /**< EtherCAT domain. */
uint32_t logical_offset, /**< Logical offset. */
size_t data_size, /**< Size of the data. */
@@ -141,48 +152,31 @@
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];
- }
-
- ec_datagram_zero(datagram);
- list_add_tail(&datagram->list, &domain->datagrams);
- datagram->domain = domain;
+ ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data,
+ data_size, used);
+ if (ret) {
+ kfree(datagram_pair);
+ return ret;
+ }
+
+ domain->expected_working_counter +=
+ datagram_pair->expected_working_counter;
+
+ EC_MASTER_DBG(domain->master, 1,
+ "Adding datagram pair with expected WC %u.\n",
+ datagram_pair->expected_working_counter);
+
+
+ list_add_tail(&datagram_pair->list, &domain->datagram_pairs);
return 0;
}
@@ -195,6 +189,8 @@
*
* Walks through the list of all FMMU configurations for the current datagram
* and ends before the current datagram.
+ *
+ * \return Non-zero if slave connfig was already counted.
*/
int shall_count(
const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to
@@ -238,7 +234,7 @@
unsigned int datagram_used[EC_DIR_COUNT];
ec_fmmu_config_t *fmmu;
const ec_fmmu_config_t *datagram_first_fmmu = NULL;
- const ec_datagram_t *datagram;
+ const ec_datagram_pair_t *datagram_pair;
int ret;
domain->logical_base_address = base_address;
@@ -272,9 +268,8 @@
// Correct logical FMMU address
fmmu->logical_start_address += base_address;
- fmmu->domain_address += base_address;
-
- // Increment input/output counter to determine datagram types
+
+ // Increment Input/Output counter to determine datagram types
// and calculate expected working counters
if (shall_count(fmmu, datagram_first_fmmu)) {
datagram_used[fmmu->dir]++;
@@ -282,8 +277,8 @@
// If the current FMMU's data do not fit in the current datagram,
// allocate a new one.
- if (datagram_size + fmmu->tx_size > EC_MAX_DATA_SIZE) {
- ret = ec_domain_add_datagram(domain,
+ if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
+ ret = ec_domain_add_datagram_pair(domain,
domain->logical_base_address + datagram_offset,
datagram_size, domain->data + datagram_offset,
datagram_used);
@@ -298,13 +293,13 @@
datagram_first_fmmu = fmmu;
}
- datagram_size += fmmu->tx_size;
- }
-
- // Allocate last datagram, if data are left (this is also the case if the
- // process data fit into a single datagram)
+ datagram_size += fmmu->data_size;
+ }
+
+ /* 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);
@@ -317,13 +312,16 @@
" %zu byte, expected working counter %u.\n", domain->index,
domain->logical_base_address, domain->data_size,
domain->expected_working_counter);
- list_for_each_entry(datagram, &domain->datagrams, list) {
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+ const ec_datagram_t *datagram =
+ &datagram_pair->datagrams[EC_DEVICE_MAIN];
EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x,"
" %zu byte, type %s.\n", datagram->name,
EC_READ_U32(datagram->address), datagram->data_size,
ec_datagram_type_string(datagram));
}
-
+
return 0;
}
@@ -346,6 +344,8 @@
/*****************************************************************************/
/** Get a certain FMMU configuration via its position in the list.
+ *
+ * \return FMMU at position \a pos, or NULL.
*/
const ec_fmmu_config_t *ec_domain_find_fmmu(
const ec_domain_t *domain, /**< EtherCAT domain. */
@@ -363,6 +363,34 @@
return NULL;
}
+/*****************************************************************************/
+
+#if EC_MAX_NUM_DEVICES > 1
+
+/** 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;
+}
+
+#endif
+
/******************************************************************************
* Application interface
*****************************************************************************/
@@ -373,7 +401,7 @@
const ec_pdo_entry_reg_t *reg;
ec_slave_config_t *sc;
int ret;
-
+
EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list("
"domain = 0x%p, regs = 0x%p)\n", domain, regs);
@@ -408,14 +436,14 @@
EC_MASTER_DBG(domain->master, 1, "ecrt_domain_external_memory("
"domain = 0x%p, mem = 0x%p)\n", domain, mem);
- ec_mutex_lock(&domain->master->master_mutex);
+ down(&domain->master->master_sem);
ec_domain_clear_data(domain);
domain->data = mem;
domain->data_origin = EC_ORIG_EXTERNAL;
- ec_mutex_unlock(&domain->master->master_mutex);
+ up(&domain->master->master_sem);
}
/*****************************************************************************/
@@ -429,20 +457,156 @@
void ecrt_domain_process(ec_domain_t *domain)
{
- uint16_t working_counter_sum;
- ec_datagram_t *datagram;
-
- working_counter_sum = 0x0000;
- list_for_each_entry(datagram, &domain->datagrams, list) {
- ec_datagram_output_stats(datagram);
- if (datagram->state == EC_DATAGRAM_RECEIVED) {
- working_counter_sum += datagram->working_counter;
- }
- }
-
- if (working_counter_sum != domain->working_counter) {
+ uint16_t wc_sum[EC_MAX_NUM_DEVICES] = {}, wc_total;
+ ec_datagram_pair_t *pair;
+#if EC_MAX_NUM_DEVICES > 1
+ uint16_t datagram_pair_wc, redundant_wc;
+ unsigned int datagram_offset;
+ ec_fmmu_config_t *fmmu = list_first_entry(&domain->fmmu_configs,
+ ec_fmmu_config_t, list);
+ unsigned int redundancy;
+#endif
+ unsigned int dev_idx;
+#ifdef EC_RT_SYSLOG
+ unsigned int wc_change;
+#endif
+
+#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) {
+#if EC_MAX_NUM_DEVICES > 1
+ datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum);
+#else
+ ec_datagram_pair_process(pair, wc_sum);
+#endif
+
+#if EC_MAX_NUM_DEVICES > 1
+ if (ec_master_num_devices(domain->master) > 1) {
+ ec_datagram_t *main_datagram = &pair->datagrams[EC_DEVICE_MAIN];
+ uint32_t logical_datagram_address =
+ EC_READ_U32(main_datagram->address);
+ size_t 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
+
+ /* Redundancy: Go through FMMU configs to detect data changes. */
+ list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) {
+ ec_datagram_t *backup_datagram =
+ &pair->datagrams[EC_DEVICE_BACKUP];
+
+ 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
+ }
+ }
+ }
+#endif // EC_MAX_NUM_DEVICES > 1
+ }
+
+#if EC_MAX_NUM_DEVICES > 1
+ redundant_wc = 0;
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ redundant_wc += wc_sum[dev_idx];
+ }
+
+ redundancy = redundant_wc > 0;
+ if (redundancy != domain->redundancy_active) {
+#ifdef EC_RT_SYSLOG
+ 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);
+ }
+#endif
+ domain->redundancy_active = redundancy;
+ }
+#else
+ domain->redundancy_active = 0;
+#endif
+
+#ifdef EC_RT_SYSLOG
+ wc_change = 0;
+#endif
+ wc_total = 0;
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ if (wc_sum[dev_idx] != domain->working_counter[dev_idx]) {
+#ifdef EC_RT_SYSLOG
+ wc_change = 1;
+#endif
+ domain->working_counter[dev_idx] = wc_sum[dev_idx];
+ }
+ wc_total += wc_sum[dev_idx];
+ }
+
+#ifdef EC_RT_SYSLOG
+ if (wc_change) {
domain->working_counter_changes++;
- domain->working_counter = working_counter_sum;
}
if (domain->working_counter_changes &&
@@ -450,26 +614,62 @@
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", domain->index,
+ wc_total, domain->expected_working_counter);
} 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", domain->index,
+ domain->working_counter_changes,
+ wc_total, domain->expected_working_counter);
+ }
+#if EC_MAX_NUM_DEVICES > 1
+ if (ec_master_num_devices(domain->master) > 1) {
+ printk(" (");
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master);
+ dev_idx++) {
+ printk("%u", domain->working_counter[dev_idx]);
+ if (dev_idx + 1 < ec_master_num_devices(domain->master)) {
+ printk("+");
+ }
+ }
+ printk(")");
+ }
+#endif
+ printk(".\n");
+
domain->working_counter_changes = 0;
}
+#endif
}
/*****************************************************************************/
void ecrt_domain_queue(ec_domain_t *domain)
{
- ec_datagram_t *datagram;
-
- list_for_each_entry(datagram, &domain->datagrams, list) {
- ec_master_queue_datagram(domain->master, datagram);
+ ec_datagram_pair_t *datagram_pair;
+ ec_device_index_t dev_idx;
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+
+#if EC_MAX_NUM_DEVICES > 1
+ /* 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);
+#endif
+ ec_master_queue_datagram(domain->master,
+ &datagram_pair->datagrams[EC_DEVICE_MAIN]);
+
+ /* copy main data to backup datagram */
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ memcpy(datagram_pair->datagrams[dev_idx].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+ ec_master_queue_datagram(domain->master,
+ &datagram_pair->datagrams[dev_idx]);
+ }
}
}
@@ -477,10 +677,18 @@
void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
{
- state->working_counter = domain->working_counter;
-
- if (domain->working_counter) {
- if (domain->working_counter == domain->expected_working_counter) {
+ unsigned int dev_idx;
+ uint16_t wc = 0;
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ wc += domain->working_counter[dev_idx];
+ }
+
+ state->working_counter = wc;
+
+ if (wc) {
+ if (wc == domain->expected_working_counter) {
state->wc_state = EC_WC_COMPLETE;
} else {
state->wc_state = EC_WC_INCOMPLETE;
@@ -488,6 +696,8 @@
} else {
state->wc_state = EC_WC_ZERO;
}
+
+ state->redundancy_active = domain->redundancy_active;
}
/*****************************************************************************/