From 9412326b8b8a3ca1b40b5e29e79f7431fd5d4292 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Mon, 16 Dec 2013 14:18:30 -0500 Subject: [PATCH 01/30] Fix spurious FoE write timeout --- master/fsm_foe.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 17b71828..2e62cdbe 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -42,9 +42,10 @@ /*****************************************************************************/ -/** Maximum time in ms to wait for responses when reading out the dictionary. +/** Maximum time in jiffies to wait for responses when reading out the + * dictionary. */ -#define EC_FSM_FOE_TIMEOUT 3000 +#define EC_FSM_FOE_TIMEOUT_JIFFIES (3 * HZ) /** Size of the FoE header. */ @@ -365,9 +366,8 @@ void ec_fsm_foe_state_ack_check( if (!ec_slave_mbox_check(fsm->datagram)) { // slave did not put anything in the mailbox yet - unsigned long diff_ms = (fsm->datagram->jiffies_received - - fsm->jiffies_start) * 1000 / HZ; - if (diff_ms >= EC_FSM_FOE_TIMEOUT) { + if (time_after(datagram->jiffies_received, + fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) { ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR); EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n"); return; @@ -704,9 +704,8 @@ void ec_fsm_foe_state_data_check( } if (!ec_slave_mbox_check(fsm->datagram)) { - unsigned long diff_ms = (fsm->datagram->jiffies_received - - fsm->jiffies_start) * 1000 / HZ; - if (diff_ms >= EC_FSM_FOE_TIMEOUT) { + if (time_after(datagram->jiffies_received, + fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) { ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR); EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n"); return; From b4b0474762c7f5d240055e2d8bd8de171277c5d8 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 22 Jan 2014 13:00:52 +1300 Subject: [PATCH 02/30] FoE: fix state transition when busy during read. Not working. --- master/fsm_foe.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 2e62cdbe..c3adfa8a 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -54,7 +54,7 @@ // uint8_t reserved // uint32_t PacketNo, Password, ErrorCode -//#define DEBUG_FOE +#define DEBUG_FOE /*****************************************************************************/ @@ -774,6 +774,7 @@ void ec_fsm_foe_state_data_read( if (ec_foe_prepare_send_ack(fsm, datagram)) { ec_foe_set_rx_error(fsm, FOE_PROT_ERROR); } + fsm->state = ec_fsm_foe_state_sent_ack; return; } @@ -886,7 +887,7 @@ void ec_fsm_foe_state_sent_ack( fsm->state = ec_fsm_foe_end; } else { - fsm->rx_expected_packet_no++; + fsm->rx_expected_packet_no++; // this should not be incremented when BUSY fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_foe_state_data_check; } From 4db1985fdf714c2e7cf7e9baa9fde9a237b78f33 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Fri, 17 Apr 2015 10:22:11 +0200 Subject: [PATCH 03/30] Undefined debugging. --- master/fsm_foe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index c3adfa8a..890e74bb 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -54,7 +54,7 @@ // uint8_t reserved // uint32_t PacketNo, Password, ErrorCode -#define DEBUG_FOE +//#define DEBUG_FOE /*****************************************************************************/ @@ -887,7 +887,7 @@ void ec_fsm_foe_state_sent_ack( fsm->state = ec_fsm_foe_end; } else { - fsm->rx_expected_packet_no++; // this should not be incremented when BUSY + fsm->rx_expected_packet_no++; fsm->retries = EC_FSM_RETRIES; fsm->state = ec_fsm_foe_state_data_check; } From d7ad55390216318be78a268964d92a956817d8d4 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 22 Jan 2014 13:01:45 +1300 Subject: [PATCH 04/30] [etherlab-dev] [PATCH] FoE: fix read packet number check --- master/fsm_foe.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 890e74bb..fe2d412e 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -733,7 +733,8 @@ void ec_fsm_foe_state_data_read( ) { size_t rec_size; - uint8_t *data, opCode, packet_no, mbox_prot; + uint32_t packet_no; + uint8_t *data, opCode, mbox_prot; ec_slave_t *slave = fsm->slave; @@ -799,9 +800,10 @@ void ec_fsm_foe_state_data_read( return; } - packet_no = EC_READ_U16(data + 2); + packet_no = EC_READ_U32(data + 2); if (packet_no != fsm->rx_expected_packet_no) { - EC_SLAVE_ERR(slave, "Received unexpected packet number.\n"); + EC_SLAVE_ERR(slave, "Received packet number %u, expected %u.\n", + packet_no, fsm->rx_expected_packet_no); ec_foe_set_rx_error(fsm, FOE_PACKETNO_ERROR); return; } From 1d1a651502fb325432b513b499bf2a42ae1fe201 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 22 Jan 2014 15:53:19 +1300 Subject: [PATCH 05/30] FoE: Fix expected packet number when busy --- master/fsm_foe.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index fe2d412e..4eeeff01 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -772,10 +772,15 @@ void ec_fsm_foe_state_data_read( opCode = EC_READ_U8(data); if (opCode == EC_FOE_OPCODE_BUSY) { + fsm->rx_expected_packet_no--; if (ec_foe_prepare_send_ack(fsm, datagram)) { ec_foe_set_rx_error(fsm, FOE_PROT_ERROR); } fsm->state = ec_fsm_foe_state_sent_ack; +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "%s() busy. Next pkt %u\n", __func__, + fsm->rx_expected_packet_no); +#endif return; } From 9ffdb2e85ba96ac18d74dbb6ba7dfd7474901811 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 22 Jan 2014 19:02:02 +1300 Subject: [PATCH 06/30] Gavin Lambert FoE Patches --- master/fsm_foe.c | 41 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 4eeeff01..76fdbc9a 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -257,6 +257,10 @@ int ec_foe_prepare_data_send( EC_WRITE_U16(data, EC_FOE_OPCODE_DATA); // OpCode = DataBlock req. EC_WRITE_U32(data + 2, fsm->tx_packet_no); // PacketNo, Password +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n", + EC_FOE_OPCODE_DATA, fsm->tx_packet_no); +#endif memcpy(data + EC_FOE_HEADER_SIZE, fsm->tx_buffer + fsm->tx_buffer_offset, current_size); @@ -294,6 +298,10 @@ int ec_foe_prepare_wrq_send( EC_WRITE_U16( data, EC_FOE_OPCODE_WRQ); // fsm write request EC_WRITE_U32( data + 2, fsm->tx_packet_no ); +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n", + EC_FOE_OPCODE_WRQ, fsm->tx_packet_no); +#endif memcpy(data + EC_FOE_HEADER_SIZE, fsm->tx_filename, current_size); @@ -431,6 +439,9 @@ void ec_fsm_foe_state_ack_read( } opCode = EC_READ_U8(data); +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "received opcode %u\n", opCode); +#endif if (opCode == EC_FOE_OPCODE_BUSY) { // slave not ready @@ -443,6 +454,20 @@ void ec_fsm_foe_state_ack_read( return; } + if (opCode == EC_FOE_OPCODE_ERR) { + fsm->request->error_code = EC_READ_U32(data + 2); + EC_SLAVE_ERR(slave, "Received FoE Error Request (code 0x%08x).\n", + fsm->request->error_code); + if (rec_size > 6 && data[6]) { + uint8_t text[256]; + strncpy(text, data + 6, min(rec_size - 6, sizeof(text))); + text[sizeof(text)-1] = 0; + EC_SLAVE_ERR(slave, "FoE Error Text: %s\n", text); + } + ec_foe_set_tx_error(fsm, FOE_OPCODE_ERROR); + return; + } + if (opCode == EC_FOE_OPCODE_ACK) { fsm->tx_packet_no++; fsm->tx_buffer_offset += fsm->tx_current_size; @@ -566,6 +591,9 @@ int ec_foe_prepare_rrq_send( EC_WRITE_U16(data, EC_FOE_OPCODE_RRQ); // fsm read request EC_WRITE_U32(data + 2, 0x00000000); // no passwd memcpy(data + EC_FOE_HEADER_SIZE, fsm->rx_filename, current_size); +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u\n", EC_FOE_OPCODE_RRQ); +#endif if (fsm->slave->master->debug_level) { EC_SLAVE_DBG(fsm->slave, 1, "FoE Read Request:\n"); @@ -596,6 +624,10 @@ int ec_foe_prepare_send_ack( EC_WRITE_U16(data, EC_FOE_OPCODE_ACK); EC_WRITE_U32(data + 2, fsm->rx_expected_packet_no); +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "sending opcode %u packet %u\n", + EC_FOE_OPCODE_ACK, fsm->rx_expected_packet_no); +#endif return 0; } @@ -770,13 +802,17 @@ void ec_fsm_foe_state_data_read( } opCode = EC_READ_U8(data); +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "received opcode %u\n", opCode); +#endif if (opCode == EC_FOE_OPCODE_BUSY) { fsm->rx_expected_packet_no--; if (ec_foe_prepare_send_ack(fsm, datagram)) { ec_foe_set_rx_error(fsm, FOE_PROT_ERROR); + } else { + fsm->state = ec_fsm_foe_state_sent_ack; } - fsm->state = ec_fsm_foe_state_sent_ack; #ifdef DEBUG_FOE EC_SLAVE_DBG(fsm->slave, 0, "%s() busy. Next pkt %u\n", __func__, fsm->rx_expected_packet_no); @@ -788,9 +824,10 @@ void ec_fsm_foe_state_data_read( fsm->request->error_code = EC_READ_U32(data + 2); EC_SLAVE_ERR(slave, "Received FoE Error Request (code 0x%08x).\n", fsm->request->error_code); - if (rec_size > 6) { + if (rec_size > 6 && data[6]) { uint8_t text[256]; strncpy(text, data + 6, min(rec_size - 6, sizeof(text))); + text[sizeof(text)-1] = 0; EC_SLAVE_ERR(slave, "FoE Error Text: %s\n", text); } ec_foe_set_rx_error(fsm, FOE_OPCODE_ERROR); From 42745f4ebb235d73773eebd8d0abc963a3b511af Mon Sep 17 00:00:00 2001 From: Dave Page Date: Wed, 5 Mar 2014 06:36:18 +1300 Subject: [PATCH 07/30] Fix fsm_foe timeout regression --- master/fsm_foe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 76fdbc9a..4470c91c 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -374,7 +374,7 @@ void ec_fsm_foe_state_ack_check( if (!ec_slave_mbox_check(fsm->datagram)) { // slave did not put anything in the mailbox yet - if (time_after(datagram->jiffies_received, + if (time_after(fsm->datagram->jiffies_received, fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) { ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR); EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n"); @@ -736,7 +736,7 @@ void ec_fsm_foe_state_data_check( } if (!ec_slave_mbox_check(fsm->datagram)) { - if (time_after(datagram->jiffies_received, + if (time_after(fsm->datagram->jiffies_received, fsm->jiffies_start + EC_FSM_FOE_TIMEOUT_JIFFIES)) { ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR); EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n"); From f9dfaf3f9bafc9fe5f387662697a66c8527c5828 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Mon, 16 Mar 2015 14:18:55 -0400 Subject: [PATCH 08/30] Reduce frequency of DC Sync debug messages --- master/fsm_foe.c | 2 +- master/fsm_slave_config.c | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) mode change 100644 => 100755 master/fsm_slave_config.c diff --git a/master/fsm_foe.c b/master/fsm_foe.c index 4470c91c..d00af29c 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -54,7 +54,7 @@ // uint8_t reserved // uint32_t PacketNo, Password, ErrorCode -//#define DEBUG_FOE +#define DEBUG_FOE /*****************************************************************************/ diff --git a/master/fsm_slave_config.c b/master/fsm_slave_config.c old mode 100644 new mode 100755 index 2ca63f0c..0dc2f8bd --- a/master/fsm_slave_config.c +++ b/master/fsm_slave_config.c @@ -1399,8 +1399,12 @@ void ec_fsm_slave_config_state_dc_sync_check( EC_SLAVE_WARN(slave, "Slave did not sync after %lu ms.\n", diff_ms); } else { - EC_SLAVE_DBG(slave, 1, "Sync after %4lu ms: %10u ns\n", - diff_ms, abs_sync_diff); + static unsigned long last_diff_ms = 0; + if ((diff_ms < last_diff_ms) || (diff_ms >= (last_diff_ms + 100))) { + last_diff_ms = diff_ms; + EC_SLAVE_DBG(slave, 1, "Sync after %4lu ms: %10u ns\n", + diff_ms, abs_sync_diff); + } // check synchrony again ec_datagram_fprd(datagram, slave->station_address, 0x092c, 4); From b8c5ff836d177d1f8e856fdc46038a5fca717e57 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Thu, 26 Mar 2015 15:16:21 -0400 Subject: [PATCH 09/30] Integrate skeleton of allow_overlapping_pdos from af21f0bdc7c9 Overlapping PDO API added, but not active/working. No more changes than required to build. EC_IOCTL_VERSION_MAGIC now 30 --- include/ecrt.h | 11 ++++++++++ lib/slave_config.c | 17 +++++++++++++++ master/ioctl.c | 49 +++++++++++++++++++++++++++++++++++++++++++ master/ioctl.h | 4 +++- master/slave_config.c | 45 ++++++++++++++++++++++++++++++++++++++- master/slave_config.h | 2 ++ 6 files changed, 126 insertions(+), 2 deletions(-) diff --git a/include/ecrt.h b/include/ecrt.h index 283adf06..cd4e1a3f 100644 --- a/include/ecrt.h +++ b/include/ecrt.h @@ -1145,6 +1145,17 @@ void ecrt_slave_config_watchdog( */ ); +/** Configure whether a slave allows overlapping PDOs. + * + * Overlapping PDOs allows inputs to use the same space as outputs on the frame. + * This reduces the frame length. + */ +void ecrt_slave_config_overlapping_pdos( + ec_slave_config_t *sc, /**< Slave configuration. */ + uint8_t allow_overlapping_pdos /**< Allow overlapping PDOs */ + ); + + /** Add a PDO to a sync manager's PDO assignment. * * This method has to be called in non-realtime context before diff --git a/lib/slave_config.c b/lib/slave_config.c index 943ca2bb..e7d5095c 100644 --- a/lib/slave_config.c +++ b/lib/slave_config.c @@ -120,6 +120,23 @@ void ecrt_slave_config_watchdog(ec_slave_config_t *sc, /*****************************************************************************/ +void ecrt_slave_config_overlapping_pdos(ec_slave_config_t *sc, + uint8_t allow_overlapping_pdos) +{ + ec_ioctl_config_t data; + + memset(&data, 0, sizeof(ec_ioctl_config_t)); + data.config_index = sc->index; + data.allow_overlapping_pdos = allow_overlapping_pdos; + + if (ioctl(sc->master->fd, EC_IOCTL_SC_OVERLAPPING_IO, &data) == -1) { + fprintf(stderr, "Failed to config overlapping PDOs: %s\n", + strerror(errno)); + } +} + +/*****************************************************************************/ + int ecrt_slave_config_pdo_assign_add(ec_slave_config_t *sc, uint8_t sync_index, uint16_t pdo_index) { diff --git a/master/ioctl.c b/master/ioctl.c index f3789ad2..6a7b8882 100644 --- a/master/ioctl.c +++ b/master/ioctl.c @@ -2254,6 +2254,48 @@ out_return: /*****************************************************************************/ +/** Configure wether a slave allows overlapping PDOs. + */ +static ATTRIBUTES int ec_ioctl_sc_allow_overlapping_pdos( + ec_master_t *master, /**< EtherCAT master. */ + void *arg, /**< ioctl() argument. */ + ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ + ) +{ + ec_ioctl_config_t data; + ec_slave_config_t *sc; + int ret = 0; + + if (unlikely(!ctx->requested)) { + ret = -EPERM; + goto out_return; + } + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + ret = -EFAULT; + goto out_return; + } + + if (down_interruptible(&master->master_sem)) { + ret = -EINTR; + goto out_return; + } + + if (!(sc = ec_master_get_config(master, data.config_index))) { + ret = -ENOENT; + goto out_up; + } + + ecrt_slave_config_overlapping_pdos(sc, + data.allow_overlapping_pdos); + +out_up: + up(&master->master_sem); +out_return: + return ret; +} +/*****************************************************************************/ + /** Add a PDO to the assignment. * * \return Zero on success, otherwise a negative error code. @@ -4447,6 +4489,13 @@ long EC_IOCTL( } ret = ec_ioctl_sc_watchdog(master, arg, ctx); break; + case EC_IOCTL_SC_OVERLAPPING_IO: + if (!ctx->writable) { + ret = -EPERM; + break; + } + ret = ec_ioctl_sc_allow_overlapping_pdos(master, arg, ctx); + break; case EC_IOCTL_SC_ADD_PDO: if (!ctx->writable) { ret = -EPERM; diff --git a/master/ioctl.h b/master/ioctl.h index 032e7f8e..8fe55e62 100644 --- a/master/ioctl.h +++ b/master/ioctl.h @@ -56,7 +56,7 @@ * * Increment this when changing the ioctl interface! */ -#define EC_IOCTL_VERSION_MAGIC 29 +#define EC_IOCTL_VERSION_MAGIC 30 // Command-line tool #define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t) @@ -153,6 +153,7 @@ #define EC_IOCTL_VOE_EXEC EC_IOWR(0x57, ec_ioctl_voe_t) #define EC_IOCTL_VOE_DATA EC_IOWR(0x58, ec_ioctl_voe_t) #define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x59, size_t) +#define EC_IOCTL_SC_OVERLAPPING_IO EC_IOW(0x5a, ec_ioctl_config_t) /*****************************************************************************/ @@ -495,6 +496,7 @@ typedef struct { int32_t slave_position; uint16_t dc_assign_activate; ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT]; + uint8_t allow_overlapping_pdos; } ec_ioctl_config_t; /*****************************************************************************/ diff --git a/master/slave_config.c b/master/slave_config.c index 38347872..3d53df7e 100644 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -70,6 +70,7 @@ void ec_slave_config_init( sc->vendor_id = vendor_id; sc->product_code = product_code; sc->watchdog_divider = 0; // use default + sc->allow_overlapping_pdos = 0; // default not allowed sc->watchdog_intervals = 0; // use default sc->slave = NULL; @@ -189,10 +190,41 @@ int ec_slave_config_prepare_fmmu( return -EOVERFLOW; } - fmmu = &sc->fmmu_configs[sc->used_fmmus++]; + fmmu = &sc->fmmu_configs[sc->used_fmmus]; down(&sc->master->master_sem); ec_fmmu_config_init(fmmu, sc, domain, sync_index, dir); + +#if 0 //TODO overlapping PDOs + // Overlapping PDO Support from 4751747d4e6d + // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + // parent code does not call ec_fmmu_config_domain + // FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME + fmmu_logical_start_address = domain->tx_size; + tx_size = fmmu->data_size; + + // FIXME is it enough to take only the *previous* FMMU into account? + + // FIXME Need to qualify allow_overlapping_pdos with slave->sii.general_flags.enable_not_lrw + + if (sc->allow_overlapping_pdos && sc->used_fmmus > 0) { + prev_fmmu = &sc->fmmu_configs[sc->used_fmmus - 1]; + if (fmmu->dir != prev_fmmu->dir && prev_fmmu->tx_size != 0) { + // prev fmmu has opposite direction + // and is not already paired with prev-prev fmmu + old_prev_tx_size = prev_fmmu->tx_size; + prev_fmmu->tx_size = max(fmmu->data_size, prev_fmmu->data_size); + domain->tx_size += prev_fmmu->tx_size - old_prev_tx_size; + tx_size = 0; + fmmu_logical_start_address = prev_fmmu->logical_start_address; + } + } + + ec_fmmu_config_domain(fmmu, domain, fmmu_logical_start_address, tx_size); + // Overlapping PDO Support from 4751747d4e6d +#endif + + sc->used_fmmus++; up(&sc->master->master_sem); return fmmu->logical_start_address; @@ -560,6 +592,17 @@ void ecrt_slave_config_watchdog(ec_slave_config_t *sc, /*****************************************************************************/ +void ecrt_slave_config_overlapping_pdos(ec_slave_config_t *sc, + uint8_t allow_overlapping_pdos ) +{ + EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, allow_overlapping_pdos = %u)\n", + __func__, sc, allow_overlapping_pdos); + + sc->allow_overlapping_pdos = allow_overlapping_pdos; +} + +/*****************************************************************************/ + int ecrt_slave_config_pdo_assign_add(ec_slave_config_t *sc, uint8_t sync_index, uint16_t pdo_index) { diff --git a/master/slave_config.h b/master/slave_config.h index 9fd633dc..a68b79a0 100644 --- a/master/slave_config.h +++ b/master/slave_config.h @@ -130,6 +130,8 @@ struct ec_slave_config { uint16_t watchdog_intervals; /**< Process data watchdog intervals (see spec. reg. 0x0420). */ + uint8_t allow_overlapping_pdos; /**< Allow input PDOs use the same frame space + as output PDOs. */ ec_slave_t *slave; /**< Slave pointer. This is \a NULL, if the slave is offline. */ From 7beb81207d1020a5be1d1525e9783229384edffd Mon Sep 17 00:00:00 2001 From: Dave Page Date: Thu, 26 Mar 2015 17:41:57 -0400 Subject: [PATCH 10/30] Refactor fmmu->logical_start_address to fmmu->logical_domain_offset --- master/domain.c | 27 +++++++++++++++------------ master/fmmu_config.c | 9 +++++---- master/fmmu_config.h | 3 ++- master/fsm_foe.c | 2 +- master/ioctl.c | 2 +- master/slave_config.c | 6 +++--- 6 files changed, 27 insertions(+), 22 deletions(-) mode change 100644 => 100755 master/domain.c mode change 100644 => 100755 master/fmmu_config.c mode change 100644 => 100755 master/fmmu_config.h mode change 100644 => 100755 master/ioctl.c mode change 100644 => 100755 master/slave_config.c diff --git a/master/domain.c b/master/domain.c old mode 100644 new mode 100755 index 6db721d2..a3baedaf --- a/master/domain.c +++ b/master/domain.c @@ -130,8 +130,8 @@ void ec_domain_add_fmmu_config( list_add_tail(&fmmu->list, &domain->fmmu_configs); EC_MASTER_DBG(domain->master, 1, "Domain %u:" - " Added %u bytes, total %zu.\n", - domain->index, fmmu->data_size, domain->data_size); + " Added %u bytes.\n", + domain->index, fmmu->data_size); } /*****************************************************************************/ @@ -237,6 +237,15 @@ int ec_domain_finish( const ec_datagram_pair_t *datagram_pair; int ret; +#if 0 + // Determine domain size from furthest extent of FMMU data + domain->data_size = 0; + list_for_each_entry(fmmu, &domain->fmmu_configs, list) { + domain->data_size = max(domain->data_size, + fmmu->logical_domain_offset + fmmu->data_size); + } +#endif + domain->logical_base_address = base_address; if (domain->data_size && domain->data_origin == EC_ORIG_INTERNAL) { @@ -265,10 +274,6 @@ int ec_domain_finish( } 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 (shall_count(fmmu, datagram_first_fmmu)) { @@ -503,19 +508,17 @@ void ecrt_domain_process(ec_domain_t *domain) continue; } - if (fmmu->logical_start_address >= - logical_datagram_address + datagram_size) { + if (fmmu->logical_domain_offset >= datagram_size) { // fmmu data contained in next datagram pair break; } - datagram_offset = - fmmu->logical_start_address - logical_datagram_address; + datagram_offset = fmmu->logical_domain_offset; #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, + "input fmmu log_off=%u size=%u offset=%u\n", + fmmu->logical_domain_offset, fmmu->data_size, datagram_offset); if (domain->master->debug_level > 0) { ec_print_data(pair->send_buffer + datagram_offset, diff --git a/master/fmmu_config.c b/master/fmmu_config.c old mode 100644 new mode 100755 index 957100f2..1c3b8042 --- a/master/fmmu_config.c +++ b/master/fmmu_config.c @@ -60,7 +60,7 @@ void ec_fmmu_config_init( fmmu->sync_index = sync_index; fmmu->dir = dir; - fmmu->logical_start_address = domain->data_size; + fmmu->logical_domain_offset = domain->data_size; fmmu->data_size = ec_pdo_list_total_size( &sc->sync_configs[sync_index].pdos); @@ -79,13 +79,14 @@ void ec_fmmu_config_page( uint8_t *data /**> Configuration page memory. */ ) { - EC_CONFIG_DBG(fmmu->sc, 1, "FMMU: LogAddr 0x%08X, Size %3u," + EC_CONFIG_DBG(fmmu->sc, 1, "FMMU: LogOff 0x%08X, Size %3u," " PhysAddr 0x%04X, SM%u, Dir %s\n", - fmmu->logical_start_address, fmmu->data_size, + fmmu->logical_domain_offset, fmmu->data_size, sync->physical_start_address, fmmu->sync_index, fmmu->dir == EC_DIR_INPUT ? "in" : "out"); - EC_WRITE_U32(data, fmmu->logical_start_address); + EC_WRITE_U32(data, fmmu->domain->logical_base_address + + fmmu->logical_domain_offset); EC_WRITE_U16(data + 4, fmmu->data_size); // size of fmmu EC_WRITE_U8 (data + 6, 0x00); // logical start bit EC_WRITE_U8 (data + 7, 0x07); // logical end bit diff --git a/master/fmmu_config.h b/master/fmmu_config.h old mode 100644 new mode 100755 index 4803f3fc..bda32cb6 --- a/master/fmmu_config.h +++ b/master/fmmu_config.h @@ -49,7 +49,8 @@ typedef struct { const ec_domain_t *domain; /**< Domain. */ uint8_t sync_index; /**< Index of sync manager to use. */ ec_direction_t dir; /**< FMMU direction. */ - uint32_t logical_start_address; /**< Logical start address. */ + uint32_t logical_domain_offset; /**< Logical offset address relative to + domain->logical_base_address. */ unsigned int data_size; /**< Covered PDO size. */ } ec_fmmu_config_t; diff --git a/master/fsm_foe.c b/master/fsm_foe.c index d00af29c..4470c91c 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -54,7 +54,7 @@ // uint8_t reserved // uint32_t PacketNo, Password, ErrorCode -#define DEBUG_FOE +//#define DEBUG_FOE /*****************************************************************************/ diff --git a/master/ioctl.c b/master/ioctl.c old mode 100644 new mode 100755 index 6a7b8882..087378dd --- a/master/ioctl.c +++ b/master/ioctl.c @@ -544,7 +544,7 @@ static ATTRIBUTES int ec_ioctl_domain_fmmu( data.slave_config_position = fmmu->sc->position; data.sync_index = fmmu->sync_index; data.dir = fmmu->dir; - data.logical_address = fmmu->logical_start_address; + data.logical_address = fmmu->domain->logical_base_address + fmmu->logical_domain_offset; data.data_size = fmmu->data_size; up(&master->master_sem); diff --git a/master/slave_config.c b/master/slave_config.c old mode 100644 new mode 100755 index 3d53df7e..e39aeef7 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -182,7 +182,7 @@ int ec_slave_config_prepare_fmmu( for (i = 0; i < sc->used_fmmus; i++) { fmmu = &sc->fmmu_configs[i]; if (fmmu->domain == domain && fmmu->sync_index == sync_index) - return fmmu->logical_start_address; + return fmmu->logical_domain_offset; } if (sc->used_fmmus == EC_MAX_FMMUS) { @@ -216,7 +216,7 @@ int ec_slave_config_prepare_fmmu( prev_fmmu->tx_size = max(fmmu->data_size, prev_fmmu->data_size); domain->tx_size += prev_fmmu->tx_size - old_prev_tx_size; tx_size = 0; - fmmu_logical_start_address = prev_fmmu->logical_start_address; + fmmu_logical_start_address = prev_fmmu->logical_domain_offset; } } @@ -227,7 +227,7 @@ int ec_slave_config_prepare_fmmu( sc->used_fmmus++; up(&sc->master->master_sem); - return fmmu->logical_start_address; + return fmmu->logical_domain_offset; } /*****************************************************************************/ From 69b5eb3a4ff712df6a46bf38f152da1a96341bd3 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Thu, 2 Apr 2015 16:49:36 -0400 Subject: [PATCH 11/30] overlapping PDO initial working. datagram size is not correct --- master/domain.c | 47 ++++++++++++++++++++++++++++++++------------ master/domain.h | 2 ++ master/fmmu_config.c | 16 ++++++++++++--- master/fmmu_config.h | 9 +++++++++ 4 files changed, 58 insertions(+), 16 deletions(-) mode change 100644 => 100755 master/domain.h diff --git a/master/domain.c b/master/domain.c index a3baedaf..8cb7e034 100755 --- a/master/domain.c +++ b/master/domain.c @@ -79,6 +79,7 @@ void ec_domain_init( domain->working_counter_changes = 0; domain->redundancy_active = 0; domain->notify_jiffies = 0; + memset(domain->offset_used, 0, sizeof(domain->offset_used)); } /*****************************************************************************/ @@ -124,14 +125,43 @@ void ec_domain_add_fmmu_config( ec_fmmu_config_t *fmmu /**< FMMU configuration. */ ) { - fmmu->domain = domain; + const ec_slave_config_t *sc; + uint32_t logical_domain_offset; + unsigned fmmu_data_size; + + fmmu->domain = domain; + sc = fmmu->sc; + + fmmu_data_size = ec_pdo_list_total_size( + &sc->sync_configs[fmmu->sync_index].pdos); + + if (sc->allow_overlapping_pdos && sc->used_fmmus) { + // If we permit overlapped PDOs, and we already have an allocated FMMU + // for this slave, alocate the subsequent FMMU offsets by direction + logical_domain_offset = domain->offset_used[fmmu->dir]; + } else { + // otherwise, allocate to the furthest extent of any allocated + // FMMU on this domain. + logical_domain_offset = max(domain->offset_used[EC_DIR_INPUT], + domain->offset_used[EC_DIR_OUTPUT]); + // rebase the free offsets to the current position + domain->offset_used[EC_DIR_INPUT] = logical_domain_offset; + domain->offset_used[EC_DIR_OUTPUT] = logical_domain_offset; + } + // consume the offset space for this FMMU's direction + domain->offset_used[fmmu->dir] += fmmu_data_size; + + ec_fmmu_set_domain_offset_size(fmmu, logical_domain_offset, fmmu_data_size); - domain->data_size += fmmu->data_size; list_add_tail(&fmmu->list, &domain->fmmu_configs); + + // Determine domain size from furthest extent of FMMU data + domain->data_size = max(domain->offset_used[EC_DIR_INPUT], + domain->offset_used[EC_DIR_OUTPUT]); EC_MASTER_DBG(domain->master, 1, "Domain %u:" - " Added %u bytes.\n", - domain->index, fmmu->data_size); + " Added %u bytes at %u.\n", + domain->index, fmmu->data_size, logical_domain_offset); } /*****************************************************************************/ @@ -237,15 +267,6 @@ int ec_domain_finish( const ec_datagram_pair_t *datagram_pair; int ret; -#if 0 - // Determine domain size from furthest extent of FMMU data - domain->data_size = 0; - list_for_each_entry(fmmu, &domain->fmmu_configs, list) { - domain->data_size = max(domain->data_size, - fmmu->logical_domain_offset + fmmu->data_size); - } -#endif - domain->logical_base_address = base_address; if (domain->data_size && domain->data_origin == EC_ORIG_INTERNAL) { diff --git a/master/domain.h b/master/domain.h old mode 100644 new mode 100755 index 1d15aaf2..ecf8ac9d --- a/master/domain.h +++ b/master/domain.h @@ -72,6 +72,8 @@ struct ec_domain since last notification. */ unsigned int redundancy_active; /**< Non-zero, if redundancy is in use. */ unsigned long notify_jiffies; /**< Time of last notification. */ + uint32_t offset_used[EC_DIR_COUNT]; /**< Next available domain offset of + PDO, by direction */ }; /*****************************************************************************/ diff --git a/master/fmmu_config.c b/master/fmmu_config.c index 1c3b8042..8c0a9643 100755 --- a/master/fmmu_config.c +++ b/master/fmmu_config.c @@ -60,13 +60,23 @@ void ec_fmmu_config_init( fmmu->sync_index = sync_index; fmmu->dir = dir; - fmmu->logical_domain_offset = domain->data_size; - fmmu->data_size = ec_pdo_list_total_size( - &sc->sync_configs[sync_index].pdos); + fmmu->logical_domain_offset = 0; + fmmu->data_size = 0; ec_domain_add_fmmu_config(domain, fmmu); } +void ec_fmmu_set_domain_offset_size( + ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */ + uint32_t logical_domain_offset, /**< Logical offset address + relative to domain->logical_base_address. */ + unsigned data_size /**< Covered PDO size. */ + ) +{ + fmmu->logical_domain_offset = logical_domain_offset; + fmmu->data_size = data_size; +} + /*****************************************************************************/ /** Initializes an FMMU configuration page. diff --git a/master/fmmu_config.h b/master/fmmu_config.h index bda32cb6..7225db01 100755 --- a/master/fmmu_config.h +++ b/master/fmmu_config.h @@ -59,6 +59,15 @@ typedef struct { void ec_fmmu_config_init(ec_fmmu_config_t *, ec_slave_config_t *, ec_domain_t *, uint8_t, ec_direction_t); +/** + * @param fmmu EtherCAT FMMU configuration. + * @param logical_domain_offset Logical offset address + relative to domain->logical_base_address. + * @param data_size Covered PDO size. +*/ +void ec_fmmu_set_domain_offset_size(ec_fmmu_config_t *fmmu, + uint32_t logical_domain_offset, unsigned data_size); + void ec_fmmu_config_page(const ec_fmmu_config_t *, const ec_sync_t *, uint8_t *); From fad06b32f1a6cd4ccc79643a1ce16d2c39a22149 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Mon, 6 Apr 2015 14:39:31 -0400 Subject: [PATCH 12/30] overlapping PDO split datagram support --- master/domain.c | 130 ++++++++++++++++++++++++++++-------------- master/domain.h | 5 ++ master/slave_config.h | 0 3 files changed, 92 insertions(+), 43 deletions(-) mode change 100644 => 100755 master/slave_config.h diff --git a/master/domain.c b/master/domain.c index 8cb7e034..ca6c8f28 100755 --- a/master/domain.c +++ b/master/domain.c @@ -79,7 +79,10 @@ void ec_domain_init( domain->working_counter_changes = 0; domain->redundancy_active = 0; domain->notify_jiffies = 0; + + /* Used by ec_domain_add_fmmu_config */ memset(domain->offset_used, 0, sizeof(domain->offset_used)); + domain->sc_in_work = 0; } /*****************************************************************************/ @@ -135,9 +138,9 @@ void ec_domain_add_fmmu_config( fmmu_data_size = ec_pdo_list_total_size( &sc->sync_configs[fmmu->sync_index].pdos); - if (sc->allow_overlapping_pdos && sc->used_fmmus) { + if (sc->allow_overlapping_pdos && (sc == domain->sc_in_work)) { // If we permit overlapped PDOs, and we already have an allocated FMMU - // for this slave, alocate the subsequent FMMU offsets by direction + // for this slave, allocate the subsequent FMMU offsets by direction logical_domain_offset = domain->offset_used[fmmu->dir]; } else { // otherwise, allocate to the furthest extent of any allocated @@ -148,13 +151,15 @@ void ec_domain_add_fmmu_config( domain->offset_used[EC_DIR_INPUT] = logical_domain_offset; domain->offset_used[EC_DIR_OUTPUT] = logical_domain_offset; } + domain->sc_in_work = sc; + // consume the offset space for this FMMU's direction domain->offset_used[fmmu->dir] += fmmu_data_size; ec_fmmu_set_domain_offset_size(fmmu, logical_domain_offset, fmmu_data_size); list_add_tail(&fmmu->list, &domain->fmmu_configs); - + // Determine domain size from furthest extent of FMMU data domain->data_size = max(domain->offset_used[EC_DIR_INPUT], domain->offset_used[EC_DIR_OUTPUT]); @@ -222,7 +227,7 @@ int ec_domain_add_datagram_pair( * * \return Non-zero if slave connfig was already counted. */ -int shall_count( +static 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. */ @@ -243,13 +248,55 @@ int shall_count( /*****************************************************************************/ +/** Domain finish helper function. + * + * Known boundaries for a datagram have been identified. Scans the datagram + * FMMU boundaries for WKC counters and then creates the datagram_pair. + * + * \param domain The parent domain + * \param datagram_begin_offset Datagram's logical beginning offset + * \param datagram_end_offset Logical end offset (one past last byte) + * \param datagram_first_fmmu The begin FMMU in the datagram + * \param datagram_end_fmmu The end (one past last) FMMU + * + * \return Non-zero if error emplacing domain + */ + static int emplace_datagram(ec_domain_t *domain, + uint32_t datagram_begin_offset, + uint32_t datagram_end_offset, + const ec_fmmu_config_t *datagram_first_fmmu, + const ec_fmmu_config_t *datagram_end_fmmu +) +{ + unsigned int datagram_used[EC_DIR_COUNT]; + const ec_fmmu_config_t *curr_fmmu; + size_t data_size; + + data_size = datagram_end_offset - datagram_begin_offset; + + memset(datagram_used, 0, sizeof(datagram_used)); + for (curr_fmmu = datagram_first_fmmu; + &curr_fmmu->list != &datagram_end_fmmu->list; + curr_fmmu = list_next_entry(curr_fmmu, list)) { + if (shall_count(curr_fmmu, datagram_first_fmmu)) { + datagram_used[curr_fmmu->dir]++; + } + } + + return ec_domain_add_datagram_pair(domain, + domain->logical_base_address + datagram_begin_offset, + data_size, + domain->data + datagram_begin_offset, + datagram_used); +} + +/*****************************************************************************/ + /** Finishes a domain. * * This allocates the necessary datagrams and writes the correct logical * addresses to every configured FMMU. * - * \todo Check for FMMUs that do not fit into any datagram. - * * \retval 0 Success * \retval <0 Error code. */ @@ -258,12 +305,13 @@ int ec_domain_finish( uint32_t base_address /**< Logical base address. */ ) { - uint32_t datagram_offset; - size_t datagram_size; - unsigned int datagram_count; - unsigned int datagram_used[EC_DIR_COUNT]; + uint32_t datagram_offset = 0; + unsigned int datagram_count = 0; ec_fmmu_config_t *fmmu; const ec_fmmu_config_t *datagram_first_fmmu = NULL; + const ec_fmmu_config_t *valid_fmmu = NULL; + unsigned candidate_start = 0; + unsigned valid_start = 0; const ec_datagram_pair_t *datagram_pair; int ret; @@ -283,52 +331,48 @@ int ec_domain_finish( // - 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; - 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) { - // Increment Input/Output counter to determine datagram types - // and calculate expected working counters - if (shall_count(fmmu, datagram_first_fmmu)) { - datagram_used[fmmu->dir]++; + if (fmmu->data_size > EC_MAX_DATA_SIZE) { + EC_MASTER_ERR(domain->master, + "FMMU size %u bytes exceeds maximum data size %u", + fmmu->data_size, EC_MAX_DATA_SIZE); + return -EINVAL; } + if (fmmu->logical_domain_offset >= candidate_start) { + // As FMMU offsets increase monotonically, and candidate start + // offset has never been contradicted, it can now never be + // contradicted, as no future FMMU can cross it. + if (candidate_start - datagram_offset > EC_MAX_DATA_SIZE) { + // yet the new candidate exceeds the datagram size, so we + // use the last known valid candidate to create the datagram + ret = emplace_datagram(domain, datagram_offset, valid_start, + datagram_first_fmmu, valid_fmmu); + if (ret < 0) + return ret; - // 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_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; - datagram_first_fmmu = fmmu; + datagram_offset = valid_start; + datagram_count++; + datagram_first_fmmu = fmmu; + } + // All FMMUs prior to this point approved for next datagram + valid_fmmu = fmmu; + valid_start = candidate_start; + } + if (fmmu->logical_domain_offset + fmmu->data_size > candidate_start) { + candidate_start = fmmu->logical_domain_offset + fmmu->data_size; } - - 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_pair(domain, - domain->logical_base_address + datagram_offset, - datagram_size, domain->data + datagram_offset, - datagram_used); + if (domain->data_size > datagram_offset) { + ret = emplace_datagram(domain, datagram_offset, domain->data_size, + datagram_first_fmmu, fmmu); if (ret < 0) return ret; datagram_count++; diff --git a/master/domain.h b/master/domain.h index ecf8ac9d..5ac9c9f3 100755 --- a/master/domain.h +++ b/master/domain.h @@ -74,6 +74,11 @@ struct ec_domain unsigned long notify_jiffies; /**< Time of last notification. */ uint32_t offset_used[EC_DIR_COUNT]; /**< Next available domain offset of PDO, by direction */ + const ec_slave_config_t *sc_in_work; /**< slave_config which is actively + being registered in this domain + (i.e. ecrt_slave_config_reg_pdo_entry() ) */ + unsigned sc_in_work_start_offset; /**< Starting domain offset of slave + config in work */ }; /*****************************************************************************/ diff --git a/master/slave_config.h b/master/slave_config.h old mode 100644 new mode 100755 From 039f9ac74dcf907d7bcbb477f1c5bd758031390f Mon Sep 17 00:00:00 2001 From: Dave Page Date: Mon, 6 Apr 2015 14:58:19 -0400 Subject: [PATCH 13/30] Remove unused variable --- master/domain.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/master/domain.h b/master/domain.h index 5ac9c9f3..1170217f 100755 --- a/master/domain.h +++ b/master/domain.h @@ -77,8 +77,6 @@ struct ec_domain const ec_slave_config_t *sc_in_work; /**< slave_config which is actively being registered in this domain (i.e. ecrt_slave_config_reg_pdo_entry() ) */ - unsigned sc_in_work_start_offset; /**< Starting domain offset of slave - config in work */ }; /*****************************************************************************/ From 8350b8587947162dd4c86e9da5c8ad02912c73c2 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Tue, 7 Apr 2015 21:07:16 -0400 Subject: [PATCH 14/30] Fix datagram overflow layout issue --- master/domain.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/master/domain.c b/master/domain.c index ca6c8f28..587dc677 100755 --- a/master/domain.c +++ b/master/domain.c @@ -347,7 +347,11 @@ int ec_domain_finish( // As FMMU offsets increase monotonically, and candidate start // offset has never been contradicted, it can now never be // contradicted, as no future FMMU can cross it. - if (candidate_start - datagram_offset > EC_MAX_DATA_SIZE) { + // All FMMUs prior to this point approved for next datagram + valid_fmmu = fmmu; + valid_start = candidate_start; + if (fmmu->logical_domain_offset + fmmu->data_size - datagram_offset + > EC_MAX_DATA_SIZE) { // yet the new candidate exceeds the datagram size, so we // use the last known valid candidate to create the datagram ret = emplace_datagram(domain, datagram_offset, valid_start, @@ -359,9 +363,6 @@ int ec_domain_finish( datagram_count++; datagram_first_fmmu = fmmu; } - // All FMMUs prior to this point approved for next datagram - valid_fmmu = fmmu; - valid_start = candidate_start; } if (fmmu->logical_domain_offset + fmmu->data_size > candidate_start) { candidate_start = fmmu->logical_domain_offset + fmmu->data_size; @@ -387,9 +388,9 @@ int ec_domain_finish( 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, + " %zu byte, type %s at %p.\n", datagram->name, EC_READ_U32(datagram->address), datagram->data_size, - ec_datagram_type_string(datagram)); + ec_datagram_type_string(datagram), datagram); } return 0; From 2fef3fdfb17fe823f7030f2c331b9a84cec878f9 Mon Sep 17 00:00:00 2001 From: Dave Page Date: Tue, 7 Apr 2015 21:25:43 -0400 Subject: [PATCH 15/30] Add DEBUG_DATAGRAM_OVERFLOW to support ec_domain_finish datagram overflow testing --- master/globals.h | 6 ++++++ 1 file changed, 6 insertions(+) mode change 100644 => 100755 master/globals.h diff --git a/master/globals.h b/master/globals.h old mode 100644 new mode 100755 index 49781838..0e99b830 --- a/master/globals.h +++ b/master/globals.h @@ -88,8 +88,14 @@ #define EC_ADDR_LEN 4 /** Resulting maximum data size of a single datagram in a frame. */ +#ifdef DEBUG_DATAGRAM_OVERFLOW +// Define a runt datagram which can be easily overflowed on +// available hardware for use when testing ec_domain_finish() +#define EC_MAX_DATA_SIZE (128) +#else #define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE \ - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_FOOTER_SIZE) +#endif // DEBUG_DATAGRAM_OVERFLOW /** Mailbox header size. */ #define EC_MBOX_HEADER_SIZE 6 From 45f66e956577b111334cdd54ba7152e7b902f611 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sat, 31 May 2014 14:49:02 +1200 Subject: [PATCH 16/30] Moved userspace domain unmap to deactivate instead of release, fixing memory/ref leak. --- lib/master.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/lib/master.c b/lib/master.c index b4809914..ee6e5719 100644 --- a/lib/master.c +++ b/lib/master.c @@ -60,6 +60,11 @@ void ec_master_clear_config(ec_master_t *master) ec_domain_t *d, *next_d; ec_slave_config_t *c, *next_c; + if (master->process_data) { + munmap(master->process_data, master->process_data_size); + master->process_data = NULL; + } + d = master->first_domain; while (d) { next_d = d->next; @@ -81,10 +86,6 @@ void ec_master_clear_config(ec_master_t *master) void ec_master_clear(ec_master_t *master) { - if (master->process_data) { - munmap(master->process_data, master->process_data_size); - } - ec_master_clear_config(master); if (master->fd != -1) { From 0aaf68055e5e748a6041540a7ca135e951bb5723 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sat, 31 May 2014 15:37:33 +1200 Subject: [PATCH 17/30] Do not force the DC reference clock to OP; if it is not configured it may refuse and flood the logs. --- configure.ac | 29 +++++++++++++++++++++++++++++ master/master.c | 2 ++ 2 files changed, 31 insertions(+) diff --git a/configure.ac b/configure.ac index fef5d141..ba631028 100644 --- a/configure.ac +++ b/configure.ac @@ -819,6 +819,35 @@ else AC_MSG_RESULT([no]) fi +#------------------------------------------------------------------------------ +# Force the reference clock to OP even if unconfigured +#------------------------------------------------------------------------------ + +AC_MSG_CHECKING([whether to force the reference clock to OP]) + +AC_ARG_ENABLE([refclkop], + AS_HELP_STRING([--enable-refclkop], + [Force reference clock to OP (default: no)]), + [ + case "${enableval}" in + yes) refclkop=1 + ;; + no) refclkop=0 + ;; + *) AC_MSG_ERROR([Invalid value for --enable-refclkop]) + ;; + esac + ], + [refclkop=0] +) + +if test "x${refclkop}" = "x1"; then + AC_DEFINE([EC_REFCLKOP], [1], [Force refclk to OP]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + #------------------------------------------------------------------------------ # Command-line tool #----------------------------------------------------------------------------- diff --git a/master/master.c b/master/master.c index 2b94b19a..d58e7161 100644 --- a/master/master.c +++ b/master/master.c @@ -2224,10 +2224,12 @@ void ec_master_request_op( } } +#ifdef EC_REFCLKOP // always set DC reference clock to OP if (master->dc_ref_clock) { ec_slave_request_state(master->dc_ref_clock, EC_SLAVE_STATE_OP); } +#endif } /****************************************************************************** From 8b034e4aa67641b2ae969998e179fc3502a4183e Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Tue, 14 Apr 2015 09:33:24 -0400 Subject: [PATCH 18/30] Ignore NXIO error otherwise this causes spam if network is empty or refclk not selected yet, and syncing refclk time to master. --- lib/master.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/master.c b/lib/master.c index ee6e5719..e0cf558f 100644 --- a/lib/master.c +++ b/lib/master.c @@ -715,11 +715,16 @@ int ecrt_master_reference_clock_time(ec_master_t *master, uint32_t *time) ret = ioctl(master->fd, EC_IOCTL_REF_CLOCK_TIME, time); if (EC_IOCTL_IS_ERROR(ret)) { - fprintf(stderr, "Failed to get reference clock time: %s\n", - strerror(EC_IOCTL_ERRNO(ret))); + ret = EC_IOCTL_ERRNO(ret); + if (ret != EIO && ret != ENXIO) { + // do not log if no refclk or no refclk time yet + fprintf(stderr, "Failed to get reference clock time: %s\n", + strerror(ret)); + } + return -ret; } - return ret; + return 0; } /****************************************************************************/ From 9e3510c455bd637b556bc1ec14ad141d4a1a24d7 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Tue, 14 Apr 2015 09:34:15 -0400 Subject: [PATCH 19/30] When detaching a slave from a slave_config, fail any pending register requests instead of leaving them BUSY forever. --- master/slave_config.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/master/slave_config.c b/master/slave_config.c index e39aeef7..32b02b70 100755 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -308,6 +308,10 @@ void ec_slave_config_detach( list_for_each_entry(reg, &sc->reg_requests, list) { if (sc->slave->fsm.reg_request == reg) { sc->slave->fsm.reg_request = NULL; + EC_SLAVE_WARN(sc->slave, "Aborting register request," + " slave is detaching.\n"); + reg->state = EC_INT_REQUEST_FAILURE; + wake_up_all(&sc->slave->master->request_queue); break; } } From 1d35113c8583d33467a978b3717dc4e1bd2f4053 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Tue, 14 Apr 2015 09:35:15 -0400 Subject: [PATCH 20/30] Abort requests started for slave_configs that don't have any actual slaves. --- master/fsm_master.c | 3 +++ master/master.c | 15 +++++++++++++++ master/master.h | 1 + master/slave_config.c | 30 ++++++++++++++++++++++++++++++ master/slave_config.h | 1 + 5 files changed, 50 insertions(+) diff --git a/master/fsm_master.c b/master/fsm_master.c index eec11a44..2988ecd2 100644 --- a/master/fsm_master.c +++ b/master/fsm_master.c @@ -246,6 +246,9 @@ void ec_fsm_master_state_start( return; } + // check for detached config requests + ec_master_expire_slave_config_requests(fsm->master); + ec_datagram_brd(fsm->datagram, 0x0130, 2); ec_datagram_zero(fsm->datagram); fsm->datagram->device_index = fsm->dev_idx; diff --git a/master/master.c b/master/master.c index d58e7161..943be201 100644 --- a/master/master.c +++ b/master/master.c @@ -1785,6 +1785,21 @@ void ec_master_attach_slave_configs( /*****************************************************************************/ +/** Abort active requests for slave configs without attached slaves. + */ +void ec_master_expire_slave_config_requests( + ec_master_t *master /**< EtherCAT master. */ + ) +{ + ec_slave_config_t *sc; + + list_for_each_entry(sc, &master->configs, list) { + ec_slave_config_expire_disconnected_requests(sc); + } +} + +/*****************************************************************************/ + /** Common implementation for ec_master_find_slave() * and ec_master_find_slave_const(). */ diff --git a/master/master.h b/master/master.h index ddd7c8d7..1446a81e 100644 --- a/master/master.h +++ b/master/master.h @@ -351,6 +351,7 @@ void ec_master_queue_datagram_ext(ec_master_t *, ec_datagram_t *); // misc. void ec_master_set_send_interval(ec_master_t *, unsigned int); void ec_master_attach_slave_configs(ec_master_t *); +void ec_master_expire_slave_config_requests(ec_master_t *); ec_slave_t *ec_master_find_slave(ec_master_t *, uint16_t, uint16_t); const ec_slave_t *ec_master_find_slave_const(const ec_master_t *, uint16_t, uint16_t); diff --git a/master/slave_config.c b/master/slave_config.c index 32b02b70..bb48423b 100755 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -553,6 +553,36 @@ ec_voe_handler_t *ec_slave_config_find_voe_handler( return NULL; } +/*****************************************************************************/ + +/** Expires any requests that have been started on a detached slave. + */ +void ec_slave_config_expire_disconnected_requests( + ec_slave_config_t *sc /**< Slave configuration. */ + ) +{ + ec_sdo_request_t *sdo_req; + ec_reg_request_t *reg_req; + + if (sc->slave) { return; } + + list_for_each_entry(sdo_req, &sc->sdo_requests, list) { + if (sdo_req->state == EC_INT_REQUEST_QUEUED || + sdo_req->state == EC_INT_REQUEST_BUSY) { + EC_CONFIG_DBG(sc, 1, "Aborting SDO request; no slave attached.\n"); + sdo_req->state = EC_INT_REQUEST_FAILURE; + } + } + + list_for_each_entry(reg_req, &sc->reg_requests, list) { + if (reg_req->state == EC_INT_REQUEST_QUEUED || + reg_req->state == EC_INT_REQUEST_BUSY) { + EC_CONFIG_DBG(sc, 1, "Aborting register request; no slave attached.\n"); + reg_req->state = EC_INT_REQUEST_FAILURE; + } + } +} + /****************************************************************************** * Application interface *****************************************************************************/ diff --git a/master/slave_config.h b/master/slave_config.h index a68b79a0..4974f2a9 100755 --- a/master/slave_config.h +++ b/master/slave_config.h @@ -174,6 +174,7 @@ ec_reg_request_t *ec_slave_config_find_reg_request(ec_slave_config_t *, unsigned int); ec_voe_handler_t *ec_slave_config_find_voe_handler(ec_slave_config_t *, unsigned int); +void ec_slave_config_expire_disconnected_requests(ec_slave_config_t *); ec_sdo_request_t *ecrt_slave_config_create_sdo_request_err( ec_slave_config_t *, uint16_t, uint8_t, size_t); From e3e96acb69ac6e205c16a33d9967f99d9a3f5b53 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sat, 31 May 2014 17:21:53 +1200 Subject: [PATCH 21/30] Fix for strings containing spaces. [04-string-download] --- tool/DataTypeHandler.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tool/DataTypeHandler.cpp b/tool/DataTypeHandler.cpp index ca7dcfed..abc55b6d 100644 --- a/tool/DataTypeHandler.cpp +++ b/tool/DataTypeHandler.cpp @@ -33,6 +33,7 @@ #include #endif +#include #include #include using namespace std; @@ -106,6 +107,8 @@ size_t DataTypeHandler::interpretAsType( stringstream str; size_t dataSize = type->byteSize; + memset(target, 0, targetSize); + #if DEBUG cerr << __func__ << "(targetSize=" << targetSize << ")" << endl; #endif @@ -192,7 +195,7 @@ size_t DataTypeHandler::interpretAsType( << dataSize << " > " << targetSize << ")"; throw SizeException(err.str()); } - str >> (char *) target; + str.read((char *) target, dataSize); break; case 0x0011: // double { From d9a93ffa249bc156d25953ea3417c647973e2990 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sat, 31 May 2014 18:08:27 +1200 Subject: [PATCH 22/30] Mark download data as const. Remove redundant code. [07-sdo-up-download] --- include/ecrt.h | 4 ++-- lib/master.c | 4 ++-- master/ioctl.c | 2 +- master/ioctl.h | 2 +- master/master.c | 4 ++-- tool/CommandDownload.cpp | 31 ++++++++++--------------------- 6 files changed, 18 insertions(+), 29 deletions(-) diff --git a/include/ecrt.h b/include/ecrt.h index cd4e1a3f..00de58b7 100644 --- a/include/ecrt.h +++ b/include/ecrt.h @@ -815,7 +815,7 @@ int ecrt_master_sdo_download( uint16_t slave_position, /**< Slave position. */ uint16_t index, /**< Index of the SDO. */ uint8_t subindex, /**< Subindex of the SDO. */ - uint8_t *data, /**< Data buffer to download. */ + const uint8_t *data, /**< Data buffer to download. */ size_t data_size, /**< Size of the data buffer. */ uint32_t *abort_code /**< Abort code of the SDO download. */ ); @@ -834,7 +834,7 @@ int ecrt_master_sdo_download_complete( ec_master_t *master, /**< EtherCAT master. */ uint16_t slave_position, /**< Slave position. */ uint16_t index, /**< Index of the SDO. */ - uint8_t *data, /**< Data buffer to download. */ + const uint8_t *data, /**< Data buffer to download. */ size_t data_size, /**< Size of the data buffer. */ uint32_t *abort_code /**< Abort code of the SDO download. */ ); diff --git a/lib/master.c b/lib/master.c index e0cf558f..8650a3a4 100644 --- a/lib/master.c +++ b/lib/master.c @@ -390,7 +390,7 @@ int ecrt_master_get_pdo_entry(ec_master_t *master, uint16_t slave_position, /****************************************************************************/ int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position, - uint16_t index, uint8_t subindex, uint8_t *data, + uint16_t index, uint8_t subindex, const uint8_t *data, size_t data_size, uint32_t *abort_code) { ec_ioctl_slave_sdo_download_t download; @@ -419,7 +419,7 @@ int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position, /****************************************************************************/ int ecrt_master_sdo_download_complete(ec_master_t *master, - uint16_t slave_position, uint16_t index, uint8_t *data, + uint16_t slave_position, uint16_t index, const uint8_t *data, size_t data_size, uint32_t *abort_code) { ec_ioctl_slave_sdo_download_t download; diff --git a/master/ioctl.c b/master/ioctl.c index 087378dd..a55a5c1a 100755 --- a/master/ioctl.c +++ b/master/ioctl.c @@ -864,7 +864,7 @@ static ATTRIBUTES int ec_ioctl_slave_sdo_download( return -ENOMEM; } - if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) { + if (copy_from_user(sdo_data, (const void __user *) data.data, data.data_size)) { kfree(sdo_data); return -EFAULT; } diff --git a/master/ioctl.h b/master/ioctl.h index 8fe55e62..b5296ea1 100644 --- a/master/ioctl.h +++ b/master/ioctl.h @@ -400,7 +400,7 @@ typedef struct { uint8_t sdo_entry_subindex; uint8_t complete_access; size_t data_size; - uint8_t *data; + const uint8_t *data; // outputs uint32_t abort_code; diff --git a/master/master.c b/master/master.c index 943be201..ebaa137d 100644 --- a/master/master.c +++ b/master/master.c @@ -2849,7 +2849,7 @@ uint32_t ecrt_master_sync_monitor_process(ec_master_t *master) /*****************************************************************************/ int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position, - uint16_t index, uint8_t subindex, uint8_t *data, + uint16_t index, uint8_t subindex, const uint8_t *data, size_t data_size, uint32_t *abort_code) { ec_sdo_request_t request; @@ -2933,7 +2933,7 @@ int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position, /*****************************************************************************/ int ecrt_master_sdo_download_complete(ec_master_t *master, - uint16_t slave_position, uint16_t index, uint8_t *data, + uint16_t slave_position, uint16_t index, const uint8_t *data, size_t data_size, uint32_t *abort_code) { ec_sdo_request_t request; diff --git a/tool/CommandDownload.cpp b/tool/CommandDownload.cpp index 0ee59d32..b69ef094 100644 --- a/tool/CommandDownload.cpp +++ b/tool/CommandDownload.cpp @@ -173,16 +173,17 @@ void CommandDownload::execute(const StringVector &args) throwCommandException(err); } data.data_size = contents.size(); - data.data = new uint8_t[data.data_size + 1]; + uint8_t *sdo_data = new uint8_t[data.data_size + 1]; + data.data = sdo_data; try { data.data_size = interpretAsType( - dataType, contents, data.data, data.data_size); + dataType, contents, sdo_data, data.data_size); } catch (SizeException &e) { - delete [] data.data; + delete [] sdo_data; throwCommandException(e.what()); } catch (ios::failure &e) { - delete [] data.data; + delete [] sdo_data; err << "Invalid value argument '" << args[2] << "' for type '" << dataType->name << "'!"; throwInvalidUsageException(err); @@ -195,35 +196,23 @@ void CommandDownload::execute(const StringVector &args) data.data_size = DefaultBufferSize; } - data.data = new uint8_t[data.data_size + 1]; + uint8_t *sdo_data = new uint8_t[data.data_size + 1]; + data.data = sdo_data; try { data.data_size = interpretAsType( - dataType, args[valueIndex], data.data, data.data_size); + dataType, args[valueIndex], sdo_data, data.data_size); } catch (SizeException &e) { - delete [] data.data; + delete [] sdo_data; throwCommandException(e.what()); } catch (ios::failure &e) { - delete [] data.data; + delete [] sdo_data; err << "Invalid value argument '" << args[2] << "' for type '" << dataType->name << "'!"; throwInvalidUsageException(err); } } - try { - data.data_size = interpretAsType( - dataType, args[valueIndex], data.data, data.data_size); - } catch (SizeException &e) { - delete [] data.data; - throwCommandException(e.what()); - } catch (ios::failure &e) { - delete [] data.data; - err << "Invalid value argument '" << args[2] - << "' for type '" << dataType->name << "'!"; - throwInvalidUsageException(err); - } - try { m.sdoDownload(&data); } catch (MasterDeviceSdoAbortException &e) { From 467bce018569807c73757522d74369726d1756d9 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sat, 31 May 2014 18:12:17 +1200 Subject: [PATCH 23/30] Add generated files to mrproper rule. [08-mrproper] --- Makefile.am | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 828a06be..d6a1653c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -119,7 +119,11 @@ mrproper: clean cleandoc configure \ configure.in \ libtool \ - stamp-h1 + stamp-h1 \ + ethercat.spec \ + script/init.d/ethercat \ + m4/*.m4 \ + `find -path ./documentation -prune -o "(" -name Makefile -o -name Makefile.in -o -name Kbuild -o -name .deps -o -name Module.symvers -o -name .tmp_versions ")" -print` doc: doxygen Doxyfile From 0dbebdbddf8f6156ab2b2a7b0e2b1976a9db2cb1 Mon Sep 17 00:00:00 2001 From: Gavin Lambert Date: Sun, 1 Jun 2014 18:07:47 +1200 Subject: [PATCH 24/30] Exit properly on "restart" request to init script. [29-init-restart] --- script/init.d/ethercat.in | 1 + 1 file changed, 1 insertion(+) diff --git a/script/init.d/ethercat.in b/script/init.d/ethercat.in index d0f981cd..12ea4547 100755 --- a/script/init.d/ethercat.in +++ b/script/init.d/ethercat.in @@ -227,6 +227,7 @@ restart) $0 stop || exit 1 sleep 1 $0 start + exit ;; status) From c4b7de94062c11458e2d42dd18a78a9e3cdcffe7 Mon Sep 17 00:00:00 2001 From: Knud Baastrup Date: Tue, 14 Apr 2015 10:12:55 -0400 Subject: [PATCH 25/30] Eoe mac address now derived from unique mac. The EoE MAC address is now derived from the NIC part of the first global unique MAC address of the linked list of available network interfaces or otherwise the MAC address used by the EtherCAT master. The EoE MAC address will get the format 02:NIC:NIC:NIC:RP:RP where NIC comes from the unique MAC address (if available) and RP is the ring position of the EoE slave. --- master/ethernet.c | 81 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/master/ethernet.c b/master/ethernet.c index 4cc70839..4d0715d4 100644 --- a/master/ethernet.c +++ b/master/ethernet.c @@ -79,6 +79,7 @@ int ec_eoedev_open(struct net_device *); int ec_eoedev_stop(struct net_device *); int ec_eoedev_tx(struct sk_buff *, struct net_device *); struct net_device_stats *ec_eoedev_stats(struct net_device *); +static int ec_eoedev_set_mac(struct net_device *netdev, void *p); /*****************************************************************************/ @@ -90,11 +91,34 @@ static const struct net_device_ops ec_eoedev_ops = { .ndo_stop = ec_eoedev_stop, .ndo_start_xmit = ec_eoedev_tx, .ndo_get_stats = ec_eoedev_stats, + .ndo_set_mac_address = ec_eoedev_set_mac, }; #endif /*****************************************************************************/ +/** + * ec_eoedev_set_mac - Change the Ethernet Address of the NIC + * @netdev: network interface device structure + * @p: pointer to an address structure + * + * Returns 0 on success, negative on failure + **/ +static int +ec_eoedev_set_mac(struct net_device *netdev, void *p) +{ + struct sockaddr *addr = p; + + if (!is_valid_ether_addr(addr->sa_data)) + return -EADDRNOTAVAIL; + + memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); + + return 0; +} + +/*****************************************************************************/ + /** EoE constructor. * * Initializes the EoE handler, creates a net_device and registers it. @@ -107,9 +131,13 @@ int ec_eoe_init( ) { ec_eoe_t **priv; - int i, ret = 0; + int ret = 0; char name[EC_DATAGRAM_NAME_SIZE]; + struct net_device *dev; + unsigned char lo_mac[ETH_ALEN] = {0}; + unsigned int use_master_mac = 0; + eoe->slave = slave; ec_datagram_init(&eoe->datagram); @@ -165,8 +193,53 @@ int ec_eoe_init( eoe->dev->get_stats = ec_eoedev_stats; #endif - for (i = 0; i < ETH_ALEN; i++) - eoe->dev->dev_addr[i] = i | (i << 4); + // First check if the MAC address assigned to the master is globally unique + if ((slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[0] & 0x02) != 0x02) { + // The master MAC is unique and the NIC part can be used for the EoE interface MAC + use_master_mac = 1; + } + else { + // The master MAC is not unique, so we check for unique MAC in other interfaces + dev = first_net_device(&init_net); + while (dev) { + // Check if globally unique MAC address + if (dev->addr_len == ETH_ALEN) { + if (memcmp(dev->dev_addr, lo_mac, ETH_ALEN) != 0) { + if ((dev->dev_addr[0] & 0x02) != 0x02) { + // The first globally unique MAC address has been identified + break; + } + } + } + dev = next_net_device(dev); + } + if (eoe->dev->addr_len == ETH_ALEN) { + if (dev) { + // A unique MAC were identified in one of the other network interfaces + // and the NIC part can be used for the EoE interface MAC. + EC_SLAVE_INFO(slave, "%s MAC address derived from NIC part of %s MAC address", + eoe->dev->name, dev->name); + eoe->dev->dev_addr[1] = dev->dev_addr[3]; + eoe->dev->dev_addr[2] = dev->dev_addr[4]; + eoe->dev->dev_addr[3] = dev->dev_addr[5]; + } + else { + use_master_mac = 1; + } + } + } + if (eoe->dev->addr_len == ETH_ALEN) { + if (use_master_mac) { + EC_SLAVE_INFO(slave, "%s MAC address derived from NIC part of %s MAC address", + eoe->dev->name, slave->master->devices[EC_DEVICE_MAIN].dev->name); + eoe->dev->dev_addr[1] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[3]; + eoe->dev->dev_addr[2] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[4]; + eoe->dev->dev_addr[3] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[5]; + } + eoe->dev->dev_addr[0] = 0x02; + eoe->dev->dev_addr[4] = (uint8_t)(slave->ring_position >> 8); + eoe->dev->dev_addr[5] = (uint8_t)(slave->ring_position); + } // initialize private data priv = netdev_priv(eoe->dev); @@ -188,8 +261,6 @@ int ec_eoe_init( goto out_free; } - // make the last address octet unique - eoe->dev->dev_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex; return 0; out_free: From 69c3f671f691a39dd81a63ff40404244439c1760 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Fri, 17 Apr 2015 11:22:17 +0200 Subject: [PATCH 26/30] Fixed line wrapping. --- master/ethernet.c | 41 +++++++++++++++++++++++++++-------------- 1 file changed, 27 insertions(+), 14 deletions(-) diff --git a/master/ethernet.c b/master/ethernet.c index 4d0715d4..ad361d3d 100644 --- a/master/ethernet.c +++ b/master/ethernet.c @@ -193,20 +193,25 @@ int ec_eoe_init( eoe->dev->get_stats = ec_eoedev_stats; #endif - // First check if the MAC address assigned to the master is globally unique - if ((slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[0] & 0x02) != 0x02) { - // The master MAC is unique and the NIC part can be used for the EoE interface MAC + // First check if the MAC address assigned to the master is globally + // unique + if ((slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[0] & 0x02) != + 0x02) { + // The master MAC is unique and the NIC part can be used for the EoE + // interface MAC use_master_mac = 1; } else { - // The master MAC is not unique, so we check for unique MAC in other interfaces + // The master MAC is not unique, so we check for unique MAC in other + // interfaces dev = first_net_device(&init_net); while (dev) { // Check if globally unique MAC address if (dev->addr_len == ETH_ALEN) { if (memcmp(dev->dev_addr, lo_mac, ETH_ALEN) != 0) { if ((dev->dev_addr[0] & 0x02) != 0x02) { - // The first globally unique MAC address has been identified + // The first globally unique MAC address has been + // identified break; } } @@ -215,9 +220,11 @@ int ec_eoe_init( } if (eoe->dev->addr_len == ETH_ALEN) { if (dev) { - // A unique MAC were identified in one of the other network interfaces - // and the NIC part can be used for the EoE interface MAC. - EC_SLAVE_INFO(slave, "%s MAC address derived from NIC part of %s MAC address", + // A unique MAC were identified in one of the other network + // interfaces and the NIC part can be used for the EoE + // interface MAC. + EC_SLAVE_INFO(slave, "%s MAC address derived from" + " NIC part of %s MAC address", eoe->dev->name, dev->name); eoe->dev->dev_addr[1] = dev->dev_addr[3]; eoe->dev->dev_addr[2] = dev->dev_addr[4]; @@ -230,11 +237,16 @@ int ec_eoe_init( } if (eoe->dev->addr_len == ETH_ALEN) { if (use_master_mac) { - EC_SLAVE_INFO(slave, "%s MAC address derived from NIC part of %s MAC address", - eoe->dev->name, slave->master->devices[EC_DEVICE_MAIN].dev->name); - eoe->dev->dev_addr[1] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[3]; - eoe->dev->dev_addr[2] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[4]; - eoe->dev->dev_addr[3] = slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[5]; + EC_SLAVE_INFO(slave, "%s MAC address derived" + " from NIC part of %s MAC address", + eoe->dev->name, + slave->master->devices[EC_DEVICE_MAIN].dev->name); + eoe->dev->dev_addr[1] = + slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[3]; + eoe->dev->dev_addr[2] = + slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[4]; + eoe->dev->dev_addr[3] = + slave->master->devices[EC_DEVICE_MAIN].dev->dev_addr[5]; } eoe->dev->dev_addr[0] = 0x02; eoe->dev->dev_addr[4] = (uint8_t)(slave->ring_position >> 8); @@ -337,7 +349,8 @@ int ec_eoe_send(ec_eoe_t *eoe /**< EoE handler */) current_size = remaining_size; last_fragment = 1; } else { - current_size = ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32; + current_size = + ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32; last_fragment = 0; } From dd49a34f8316aa61b3bbfbd117a065f21775180d Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Fri, 17 Apr 2015 11:32:54 +0200 Subject: [PATCH 27/30] Defined list_next_entry for older kernels. --- master/domain.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/master/domain.c b/master/domain.c index 587dc677..7bdb1583 100755 --- a/master/domain.c +++ b/master/domain.c @@ -47,6 +47,11 @@ */ #define DEBUG_REDUNDANCY 0 +#ifndef list_next_entry +#define list_next_entry(pos, member) \ + list_entry((pos)->member.next, typeof(*(pos)), member) +#endif + /*****************************************************************************/ void ec_domain_clear_data(ec_domain_t *); @@ -261,12 +266,12 @@ static int shall_count( * * \return Non-zero if error emplacing domain */ - static int emplace_datagram(ec_domain_t *domain, +static int emplace_datagram(ec_domain_t *domain, uint32_t datagram_begin_offset, uint32_t datagram_end_offset, const ec_fmmu_config_t *datagram_first_fmmu, const ec_fmmu_config_t *datagram_end_fmmu -) + ) { unsigned int datagram_used[EC_DIR_COUNT]; const ec_fmmu_config_t *curr_fmmu; From fd9e5a770e6fcef0d30a36cb75915b217ba667bb Mon Sep 17 00:00:00 2001 From: Knud Baastrup Date: Tue, 14 Apr 2015 13:10:55 -0400 Subject: [PATCH 28/30] Maximum length of foe filename extended to 255. --- master/ioctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/master/ioctl.h b/master/ioctl.h index b5296ea1..de3378ff 100644 --- a/master/ioctl.h +++ b/master/ioctl.h @@ -440,7 +440,7 @@ typedef struct { size_t data_size; uint32_t result; uint32_t error_code; - char file_name[32]; + char file_name[255]; } ec_ioctl_slave_foe_t; /*****************************************************************************/ From ff75554c58084bda9504063066c2234629c874cc Mon Sep 17 00:00:00 2001 From: Knud Baastrup Date: Tue, 14 Apr 2015 13:12:24 -0400 Subject: [PATCH 29/30] Internal SDO requests now synchronized with external requests. Internal SDO requests are managed by master FSM and can conflict with external requests managed by slave FSM. The internal SDO requests includes SDO requests created by an application and external request are typical created by EtherCAT Tool for SDO upload/download or a directory fetch initiated with ethercat sdos command. The conflict will cause a FPWR from an external request to be overwritten by a FPWR from an internal SDO request (or oppersite) in the same "train" of datagrams. --- master/fsm_master.c | 5 +++++ master/fsm_slave.c | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/master/fsm_master.c b/master/fsm_master.c index 2988ecd2..e6bf1362 100644 --- a/master/fsm_master.c +++ b/master/fsm_master.c @@ -493,6 +493,11 @@ int ec_fsm_master_action_process_sdo( continue; } + if (!ec_fsm_slave_is_ready(&slave->fsm)) { + EC_SLAVE_DBG(slave, 1, "Busy - processing external request!\n"); + continue; + } + list_for_each_entry(req, &slave->config->sdo_requests, list) { if (req->state == EC_INT_REQUEST_QUEUED) { diff --git a/master/fsm_slave.c b/master/fsm_slave.c index 67315cf2..fc21574f 100644 --- a/master/fsm_slave.c +++ b/master/fsm_slave.c @@ -201,6 +201,18 @@ void ec_fsm_slave_state_ready( ec_datagram_t *datagram /**< Datagram to use. */ ) { + ec_slave_t *slave = fsm->slave; + ec_sdo_request_t *req; + + if (slave->config) { + list_for_each_entry(req, &slave->config->sdo_requests, list) { + if (req->state == EC_INT_REQUEST_QUEUED || req->state == EC_INT_REQUEST_BUSY) { + EC_SLAVE_DBG(slave, 1, "Busy - processing internal SDO request!\n"); + return; + } + } + } + // Check for pending external SDO requests if (ec_fsm_slave_action_process_sdo(fsm, datagram)) { return; From 45839e86663899555bd28a7464c88e4175f77bfe Mon Sep 17 00:00:00 2001 From: Dave Page Date: Tue, 14 Apr 2015 16:25:44 -0400 Subject: [PATCH 30/30] 16-ethercat-1.5-frame-corruption.patch from ethercat-1.5.0-patches-v2.tar.bz2 by Frank Heckenbach f.heckenbach@ at fh-soft.de Ref: http://lists.etherlab.org/pipermail/etherlab-dev/2014/000404.html with some additional edits. --- include/ecrt.h | 4 +++- lib/master.c | 7 +++++-- master/device.h | 2 +- master/ioctl.c | 9 ++++++++- master/master.c | 28 ++++++++++++++++------------ 5 files changed, 33 insertions(+), 17 deletions(-) diff --git a/include/ecrt.h b/include/ecrt.h index 00de58b7..c306278f 100644 --- a/include/ecrt.h +++ b/include/ecrt.h @@ -956,8 +956,10 @@ int ecrt_master_set_send_interval( * * Has to be called cyclically by the application after ecrt_master_activate() * has returned. + * + * Returns the number of bytes sent. */ -void ecrt_master_send( +size_t ecrt_master_send( ec_master_t *master /**< EtherCAT master. */ ); diff --git a/lib/master.c b/lib/master.c index 8650a3a4..7c218428 100644 --- a/lib/master.c +++ b/lib/master.c @@ -607,15 +607,18 @@ int ecrt_master_set_send_interval(ec_master_t *master, /****************************************************************************/ -void ecrt_master_send(ec_master_t *master) +size_t ecrt_master_send(ec_master_t *master) { int ret; + size_t sent_bytes = 0; - ret = ioctl(master->fd, EC_IOCTL_SEND, NULL); + ret = ioctl(master->fd, EC_IOCTL_SEND, &sent_bytes); if (EC_IOCTL_IS_ERROR(ret)) { fprintf(stderr, "Failed to send: %s\n", strerror(EC_IOCTL_ERRNO(ret))); } + + return sent_bytes; } /****************************************************************************/ diff --git a/master/device.h b/master/device.h index 0a0e889b..641a38ae 100644 --- a/master/device.h +++ b/master/device.h @@ -48,7 +48,7 @@ * different memory regions, because otherwise the network device DMA could * send the same data twice, if it is called twice. */ -#define EC_TX_RING_SIZE 2 +#define EC_TX_RING_SIZE 0x10 #ifdef EC_DEBUG_IF #include "debug.h" diff --git a/master/ioctl.c b/master/ioctl.c index a55a5c1a..ce675622 100755 --- a/master/ioctl.c +++ b/master/ioctl.c @@ -1913,11 +1913,18 @@ static ATTRIBUTES int ec_ioctl_send( ec_ioctl_context_t *ctx /**< Private data structure of file handle. */ ) { + size_t sent_bytes; + if (unlikely(!ctx->requested)) { return -EPERM; } - ecrt_master_send(master); + sent_bytes = ecrt_master_send(master); + + if (copy_to_user((void __user *) arg, &sent_bytes, sizeof(sent_bytes))) { + return -EFAULT; + } + return 0; } diff --git a/master/master.c b/master/master.c index ebaa137d..7575ff13 100644 --- a/master/master.c +++ b/master/master.c @@ -981,7 +981,7 @@ void ec_master_queue_datagram_ext( /** Sends the datagrams in the queue for a certain device. * */ -void ec_master_send_datagrams( +size_t ec_master_send_datagrams( ec_master_t *master, /**< EtherCAT master */ ec_device_index_t device_index /**< Device index. */ ) @@ -996,6 +996,7 @@ void ec_master_send_datagrams( unsigned long jiffies_sent; unsigned int frame_count, more_datagrams_waiting; struct list_head sent_datagrams; + size_t sent_bytes = 0; #ifdef EC_HAVE_CYCLES cycles_start = get_cycles(); @@ -1081,6 +1082,8 @@ void ec_master_send_datagrams( // send frame ec_device_send(&master->devices[device_index], cur_data - frame_data); + /* preamble and inter-frame gap */ + sent_bytes += ETH_HLEN + cur_data - frame_data + ETH_FCS_LEN + 20; #ifdef EC_HAVE_CYCLES cycles_sent = get_cycles(); #endif @@ -1098,7 +1101,7 @@ void ec_master_send_datagrams( frame_count++; } - while (more_datagrams_waiting); + while (more_datagrams_waiting && frame_count < EC_TX_RING_SIZE); #ifdef EC_HAVE_CYCLES if (unlikely(master->debug_level > 1)) { @@ -1108,6 +1111,7 @@ void ec_master_send_datagrams( (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz); } #endif + return sent_bytes; } /*****************************************************************************/ @@ -1535,9 +1539,7 @@ static int ec_master_idle_thread(void *priv_data) { ec_master_t *master = (ec_master_t *) priv_data; int fsm_exec; -#ifdef EC_USE_HRTIMER size_t sent_bytes; -#endif // send interval in IDLE phase ec_master_set_send_interval(master, 1000000 / HZ); @@ -1570,11 +1572,7 @@ static int ec_master_idle_thread(void *priv_data) if (fsm_exec) { ec_master_queue_datagram(master, &master->fsm_datagram); } - ecrt_master_send(master); -#ifdef EC_USE_HRTIMER - sent_bytes = master->devices[EC_DEVICE_MAIN].tx_skb[ - master->devices[EC_DEVICE_MAIN].tx_ring_index]->len; -#endif + sent_bytes = ecrt_master_send(master); up(&master->io_sem); if (ec_fsm_master_idle(&master->fsm)) { @@ -1586,7 +1584,8 @@ static int ec_master_idle_thread(void *priv_data) #endif } else { #ifdef EC_USE_HRTIMER - ec_master_nanosleep(sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS); + ec_master_nanosleep( + sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS * 6 / 5); #else schedule(); #endif @@ -2447,10 +2446,12 @@ void ecrt_master_deactivate(ec_master_t *master) /*****************************************************************************/ -void ecrt_master_send(ec_master_t *master) +size_t ecrt_master_send(ec_master_t *master) { ec_datagram_t *datagram, *n; ec_device_index_t dev_idx; + size_t sent_bytes = 0; + if (master->injection_seq_rt != master->injection_seq_fsm) { // inject datagram produced by master FSM @@ -2485,8 +2486,11 @@ void ecrt_master_send(ec_master_t *master) } // send frames - ec_master_send_datagrams(master, dev_idx); + sent_bytes = max(sent_bytes, + ec_master_send_datagrams(master, dev_idx)); } + + return sent_bytes; } /*****************************************************************************/