# HG changeset patch # User Florian Pose # Date 1453464583 -3600 # Node ID 42b62867574ddbf3017a65f86a26704dbc82a194 # Parent f859d567f94e1a63731863fafc0747b27daf1bc1# Parent a380cce7d6f0edfd884dff114f8ec5812287a629 merge diff -r f859d567f94e -r 42b62867574d Makefile.am --- a/Makefile.am Fri Jan 22 10:11:58 2016 +0100 +++ b/Makefile.am Fri Jan 22 13:09:43 2016 +0100 @@ -119,7 +119,11 @@ 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 diff -r f859d567f94e -r 42b62867574d configure.ac --- a/configure.ac Fri Jan 22 10:11:58 2016 +0100 +++ b/configure.ac Fri Jan 22 13:09:43 2016 +0100 @@ -820,6 +820,35 @@ 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 -r f859d567f94e -r 42b62867574d include/ecrt.h --- a/include/ecrt.h Fri Jan 22 10:11:58 2016 +0100 +++ b/include/ecrt.h Fri Jan 22 13:09:43 2016 +0100 @@ -815,7 +815,7 @@ 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 @@ 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. */ ); @@ -956,8 +956,10 @@ * * Has to be called cyclically by the application after ecrt_master_activate() * has returned. - */ -void ecrt_master_send( + * + * Returns the number of bytes sent. + */ +size_t ecrt_master_send( ec_master_t *master /**< EtherCAT master. */ ); @@ -1145,6 +1147,17 @@ */ ); +/** 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 -r f859d567f94e -r 42b62867574d lib/master.c --- a/lib/master.c Fri Jan 22 10:11:58 2016 +0100 +++ b/lib/master.c Fri Jan 22 13:09:43 2016 +0100 @@ -60,6 +60,11 @@ 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(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) { @@ -389,7 +390,7 @@ /****************************************************************************/ 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; @@ -418,7 +419,7 @@ /****************************************************************************/ 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; @@ -606,15 +607,18 @@ /****************************************************************************/ -void ecrt_master_send(ec_master_t *master) -{ - int ret; - - ret = ioctl(master->fd, EC_IOCTL_SEND, NULL); +size_t ecrt_master_send(ec_master_t *master) +{ + int ret; + size_t sent_bytes = 0; + + 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; } /****************************************************************************/ @@ -714,11 +718,16 @@ 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))); - } - - return 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 0; } /****************************************************************************/ diff -r f859d567f94e -r 42b62867574d lib/slave_config.c --- a/lib/slave_config.c Fri Jan 22 10:11:58 2016 +0100 +++ b/lib/slave_config.c Fri Jan 22 13:09:43 2016 +0100 @@ -120,6 +120,23 @@ /*****************************************************************************/ +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 -r f859d567f94e -r 42b62867574d master/device.h --- a/master/device.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/device.h Fri Jan 22 13:09:43 2016 +0100 @@ -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 -r f859d567f94e -r 42b62867574d master/domain.c --- a/master/domain.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/domain.c Fri Jan 22 13:09:43 2016 +0100 @@ -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 *); @@ -79,6 +84,10 @@ 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; } /*****************************************************************************/ @@ -124,14 +133,45 @@ ec_fmmu_config_t *fmmu /**< FMMU configuration. */ ) { + const ec_slave_config_t *sc; + uint32_t logical_domain_offset; + unsigned fmmu_data_size; + fmmu->domain = domain; - - domain->data_size += fmmu->data_size; + sc = fmmu->sc; + + fmmu_data_size = ec_pdo_list_total_size( + &sc->sync_configs[fmmu->sync_index].pdos); + + 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, 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 + // 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; + } + 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]); + 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 at %u.\n", + domain->index, fmmu->data_size, logical_domain_offset); } /*****************************************************************************/ @@ -192,7 +232,7 @@ * * \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. */ @@ -213,13 +253,55 @@ /*****************************************************************************/ +/** 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. */ @@ -228,12 +310,13 @@ 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; @@ -253,56 +336,49 @@ // - 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) { - - // 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)) { - datagram_used[fmmu->dir]++; - } - - // 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_size += fmmu->data_size; + 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. + // 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, + datagram_first_fmmu, valid_fmmu); + if (ret < 0) + return ret; + + datagram_offset = valid_start; + datagram_count++; + datagram_first_fmmu = fmmu; + } + } + if (fmmu->logical_domain_offset + fmmu->data_size > candidate_start) { + candidate_start = fmmu->logical_domain_offset + 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++; @@ -317,9 +393,9 @@ 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; @@ -503,19 +579,17 @@ 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 -r f859d567f94e -r 42b62867574d master/domain.h --- a/master/domain.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/domain.h Fri Jan 22 13:09:43 2016 +0100 @@ -72,6 +72,11 @@ 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 */ + 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() ) */ }; /*****************************************************************************/ diff -r f859d567f94e -r 42b62867574d master/ethernet.c --- a/master/ethernet.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/ethernet.c Fri Jan 22 13:09:43 2016 +0100 @@ -79,6 +79,7 @@ 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 @@ .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 @@ ) { 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,65 @@ 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 +273,6 @@ 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: @@ -266,7 +349,8 @@ 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; } diff -r f859d567f94e -r 42b62867574d master/fmmu_config.c --- a/master/fmmu_config.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fmmu_config.c Fri Jan 22 13:09:43 2016 +0100 @@ -60,13 +60,23 @@ fmmu->sync_index = sync_index; fmmu->dir = dir; - fmmu->logical_start_address = 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. @@ -79,13 +89,14 @@ 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 -r f859d567f94e -r 42b62867574d master/fmmu_config.h --- a/master/fmmu_config.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fmmu_config.h Fri Jan 22 13:09:43 2016 +0100 @@ -49,7 +49,8 @@ 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; @@ -58,6 +59,15 @@ 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 *); diff -r f859d567f94e -r 42b62867574d master/fsm_foe.c --- a/master/fsm_foe.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fsm_foe.c Fri Jan 22 13:09:43 2016 +0100 @@ -42,9 +42,10 @@ /*****************************************************************************/ -/** Maximum time in ms to wait for responses when reading out the dictionary. - */ -#define EC_FSM_FOE_TIMEOUT 3000 +/** Maximum time in jiffies to wait for responses when reading out the + * dictionary. + */ +#define EC_FSM_FOE_TIMEOUT_JIFFIES (3 * HZ) /** Size of the FoE header. */ @@ -256,6 +257,10 @@ 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); @@ -293,6 +298,10 @@ 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); @@ -365,9 +374,8 @@ 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(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"); return; @@ -431,6 +439,9 @@ } 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 @@ 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 @@ 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 @@ 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; } @@ -704,9 +736,8 @@ } 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(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"); return; @@ -734,7 +765,8 @@ ) { 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; @@ -770,11 +802,21 @@ } 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; } +#ifdef DEBUG_FOE + EC_SLAVE_DBG(fsm->slave, 0, "%s() busy. Next pkt %u\n", __func__, + fsm->rx_expected_packet_no); +#endif return; } @@ -782,9 +824,10 @@ 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); @@ -799,9 +842,10 @@ 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; } diff -r f859d567f94e -r 42b62867574d master/fsm_master.c --- a/master/fsm_master.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fsm_master.c Fri Jan 22 13:09:43 2016 +0100 @@ -246,6 +246,9 @@ 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; @@ -490,6 +493,11 @@ 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 -r f859d567f94e -r 42b62867574d master/fsm_slave.c --- a/master/fsm_slave.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fsm_slave.c Fri Jan 22 13:09:43 2016 +0100 @@ -201,6 +201,18 @@ 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; diff -r f859d567f94e -r 42b62867574d master/fsm_slave_config.c --- a/master/fsm_slave_config.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/fsm_slave_config.c Fri Jan 22 13:09:43 2016 +0100 @@ -1399,8 +1399,12 @@ 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); diff -r f859d567f94e -r 42b62867574d master/globals.h --- a/master/globals.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/globals.h Fri Jan 22 13:09:43 2016 +0100 @@ -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 diff -r f859d567f94e -r 42b62867574d master/ioctl.c --- a/master/ioctl.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/ioctl.c Fri Jan 22 13:09:43 2016 +0100 @@ -544,7 +544,7 @@ 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); @@ -864,7 +864,7 @@ 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; } @@ -1913,11 +1913,18 @@ 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; } @@ -2254,6 +2261,48 @@ /*****************************************************************************/ +/** 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 +4496,13 @@ } 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 -r f859d567f94e -r 42b62867574d master/ioctl.h --- a/master/ioctl.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/ioctl.h Fri Jan 22 13:09:43 2016 +0100 @@ -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) /*****************************************************************************/ @@ -399,7 +400,7 @@ 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; @@ -439,7 +440,7 @@ size_t data_size; uint32_t result; uint32_t error_code; - char file_name[32]; + char file_name[255]; } ec_ioctl_slave_foe_t; /*****************************************************************************/ @@ -495,6 +496,7 @@ 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 -r f859d567f94e -r 42b62867574d master/master.c --- a/master/master.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/master.c Fri Jan 22 13:09:43 2016 +0100 @@ -981,7 +981,7 @@ /** 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 @@ 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 @@ // 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 @@ 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 @@ (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz); } #endif + return sent_bytes; } /*****************************************************************************/ @@ -1535,9 +1539,7 @@ { 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 @@ 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 @@ #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 @@ -1785,6 +1784,21 @@ /*****************************************************************************/ +/** 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(). */ @@ -2224,10 +2238,12 @@ } } +#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 } /****************************************************************************** @@ -2430,10 +2446,12 @@ /*****************************************************************************/ -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 @@ -2468,8 +2486,11 @@ } // send frames - ec_master_send_datagrams(master, dev_idx); - } + sent_bytes = max(sent_bytes, + ec_master_send_datagrams(master, dev_idx)); + } + + return sent_bytes; } /*****************************************************************************/ @@ -2832,7 +2853,7 @@ /*****************************************************************************/ 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; @@ -2916,7 +2937,7 @@ /*****************************************************************************/ 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 -r f859d567f94e -r 42b62867574d master/master.h --- a/master/master.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/master.h Fri Jan 22 13:09:43 2016 +0100 @@ -351,6 +351,7 @@ // 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 -r f859d567f94e -r 42b62867574d master/slave_config.c --- a/master/slave_config.c Fri Jan 22 10:11:58 2016 +0100 +++ b/master/slave_config.c Fri Jan 22 13:09:43 2016 +0100 @@ -70,6 +70,7 @@ 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; @@ -181,7 +182,7 @@ 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) { @@ -189,13 +190,44 @@ 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_domain_offset; + } + } + + 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; + return fmmu->logical_domain_offset; } /*****************************************************************************/ @@ -276,6 +308,10 @@ 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; } } @@ -517,6 +553,36 @@ 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 *****************************************************************************/ @@ -560,6 +626,17 @@ /*****************************************************************************/ +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 -r f859d567f94e -r 42b62867574d master/slave_config.h --- a/master/slave_config.h Fri Jan 22 10:11:58 2016 +0100 +++ b/master/slave_config.h Fri Jan 22 13:09:43 2016 +0100 @@ -130,6 +130,8 @@ 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. */ @@ -172,6 +174,7 @@ 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); diff -r f859d567f94e -r 42b62867574d script/init.d/ethercat.in --- a/script/init.d/ethercat.in Fri Jan 22 10:11:58 2016 +0100 +++ b/script/init.d/ethercat.in Fri Jan 22 13:09:43 2016 +0100 @@ -227,6 +227,7 @@ $0 stop || exit 1 sleep 1 $0 start + exit ;; status) diff -r f859d567f94e -r 42b62867574d tool/CommandDownload.cpp --- a/tool/CommandDownload.cpp Fri Jan 22 10:11:58 2016 +0100 +++ b/tool/CommandDownload.cpp Fri Jan 22 13:09:43 2016 +0100 @@ -173,16 +173,17 @@ 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,16 +196,17 @@ 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); @@ -212,19 +214,6 @@ } 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) { delete [] data.data; diff -r f859d567f94e -r 42b62867574d tool/DataTypeHandler.cpp --- a/tool/DataTypeHandler.cpp Fri Jan 22 10:11:58 2016 +0100 +++ b/tool/DataTypeHandler.cpp Fri Jan 22 13:09:43 2016 +0100 @@ -33,6 +33,7 @@ #include #endif +#include #include #include using namespace std; @@ -106,6 +107,8 @@ stringstream str; size_t dataSize = type->byteSize; + memset(target, 0, targetSize); + #if DEBUG cerr << __func__ << "(targetSize=" << targetSize << ")" << endl; #endif @@ -192,7 +195,7 @@ << dataSize << " > " << targetSize << ")"; throw SizeException(err.str()); } - str >> (char *) target; + str.read((char *) target, dataSize); break; case 0x0011: // double {