fp@42: /****************************************************************************** fp@42: * fp@42: * $Id$ fp@42: * fp@1326: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@1326: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1326: * modify it under the terms of the GNU General Public License version 2, as fp@1326: * published by the Free Software Foundation. fp@1326: * fp@1326: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1326: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1326: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1326: * Public License for more details. fp@1326: * fp@1326: * You should have received a copy of the GNU General Public License along fp@1326: * with the IgH EtherCAT Master; if not, write to the Free Software fp@197: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@197: * fp@1363: * --- fp@1363: * fp@1363: * The license mentioned above concerns the source code only. Using the fp@1363: * EtherCAT technology and brand is only permitted in compliance with the fp@1363: * industrial property and similar rights of Beckhoff Automation GmbH. fp@246: * fp@42: *****************************************************************************/ fp@42: fp@199: /** fp@199: \file fp@199: EtherCAT domain methods. fp@199: */ fp@199: fp@199: /*****************************************************************************/ fp@199: fp@294: #include fp@294: fp@54: #include "globals.h" fp@792: #include "master.h" fp@792: #include "slave_config.h" fp@792: fp@54: #include "domain.h" fp@2589: #include "datagram_pair.h" fp@2589: fp@2589: /** Extra debug output for redundancy functions. fp@2589: */ fp@2589: #define DEBUG_REDUNDANCY 0 fp@325: fp@325: /*****************************************************************************/ fp@325: fp@809: void ec_domain_clear_data(ec_domain_t *); fp@199: fp@178: /*****************************************************************************/ fp@178: fp@792: /** Domain constructor. fp@993: */ fp@993: void ec_domain_init( fp@792: ec_domain_t *domain, /**< EtherCAT domain. */ fp@792: ec_master_t *master, /**< Parent master. */ fp@792: unsigned int index /**< Index. */ fp@792: ) fp@73: { fp@2589: unsigned int dev_idx; fp@2589: fp@73: domain->master = master; fp@178: domain->index = index; fp@985: INIT_LIST_HEAD(&domain->fmmu_configs); fp@73: domain->data_size = 0; fp@809: domain->data = NULL; fp@809: domain->data_origin = EC_ORIG_INTERNAL; fp@985: domain->logical_base_address = 0x00000000; fp@2589: INIT_LIST_HEAD(&domain->datagram_pairs); fp@2589: for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master); fp@2589: dev_idx++) { fp@2589: domain->working_counter[dev_idx] = 0x0000; fp@2589: } fp@985: domain->expected_working_counter = 0x0000; fp@818: domain->working_counter_changes = 0; fp@2589: domain->redundancy_active = 0; fp@344: domain->notify_jiffies = 0; dave@2612: dave@2612: /* Used by ec_domain_add_fmmu_config */ dave@2611: memset(domain->offset_used, 0, sizeof(domain->offset_used)); dave@2612: domain->sc_in_work = 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@818: /** Domain destructor. fp@993: */ fp@993: void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */) fp@993: { fp@2589: ec_datagram_pair_t *datagram_pair, *next_pair; fp@993: fp@993: // dequeue and free datagrams fp@2589: list_for_each_entry_safe(datagram_pair, next_pair, fp@2589: &domain->datagram_pairs, list) { fp@2589: ec_datagram_pair_clear(datagram_pair); fp@2589: kfree(datagram_pair); fp@144: } fp@98: fp@809: ec_domain_clear_data(domain); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@809: /** Frees internally allocated memory. fp@809: */ fp@809: void ec_domain_clear_data( fp@809: ec_domain_t *domain /**< EtherCAT domain. */ fp@809: ) fp@809: { fp@2101: if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) { fp@809: kfree(domain->data); fp@2101: } fp@2589: fp@809: domain->data = NULL; fp@809: domain->data_origin = EC_ORIG_INTERNAL; fp@809: } fp@809: fp@809: /*****************************************************************************/ fp@809: fp@818: /** Adds an FMMU configuration to the domain. fp@818: */ fp@818: void ec_domain_add_fmmu_config( fp@818: ec_domain_t *domain, /**< EtherCAT domain. */ fp@818: ec_fmmu_config_t *fmmu /**< FMMU configuration. */ fp@818: ) fp@818: { dave@2611: const ec_slave_config_t *sc; dave@2611: uint32_t logical_domain_offset; dave@2611: unsigned fmmu_data_size; dave@2611: fp@818: fmmu->domain = domain; dave@2611: sc = fmmu->sc; dave@2611: dave@2611: fmmu_data_size = ec_pdo_list_total_size( dave@2611: &sc->sync_configs[fmmu->sync_index].pdos); dave@2611: dave@2612: if (sc->allow_overlapping_pdos && (sc == domain->sc_in_work)) { dave@2611: // If we permit overlapped PDOs, and we already have an allocated FMMU dave@2612: // for this slave, allocate the subsequent FMMU offsets by direction dave@2611: logical_domain_offset = domain->offset_used[fmmu->dir]; dave@2611: } else { dave@2611: // otherwise, allocate to the furthest extent of any allocated dave@2611: // FMMU on this domain. dave@2611: logical_domain_offset = max(domain->offset_used[EC_DIR_INPUT], dave@2611: domain->offset_used[EC_DIR_OUTPUT]); dave@2611: // rebase the free offsets to the current position dave@2611: domain->offset_used[EC_DIR_INPUT] = logical_domain_offset; dave@2611: domain->offset_used[EC_DIR_OUTPUT] = logical_domain_offset; dave@2611: } dave@2612: domain->sc_in_work = sc; dave@2612: dave@2611: // consume the offset space for this FMMU's direction dave@2611: domain->offset_used[fmmu->dir] += fmmu_data_size; dave@2611: dave@2611: ec_fmmu_set_domain_offset_size(fmmu, logical_domain_offset, fmmu_data_size); dave@2611: fp@950: list_add_tail(&fmmu->list, &domain->fmmu_configs); dave@2612: dave@2611: // Determine domain size from furthest extent of FMMU data dave@2611: domain->data_size = max(domain->offset_used[EC_DIR_INPUT], dave@2611: domain->offset_used[EC_DIR_OUTPUT]); fp@912: fp@1921: EC_MASTER_DBG(domain->master, 1, "Domain %u:" dave@2611: " Added %u bytes at %u.\n", dave@2611: domain->index, fmmu->data_size, logical_domain_offset); fp@818: } fp@818: fp@818: /*****************************************************************************/ fp@818: fp@2589: /** Allocates a domain datagram pair and appends it to the list. fp@2589: * fp@2589: * The datagrams' types and expected working counters are determined by the fp@2589: * number of input and output fmmus that share the datagrams. fp@985: * fp@1313: * \retval 0 Success. fp@1313: * \retval <0 Error code. fp@635: */ fp@2589: int ec_domain_add_datagram_pair( fp@792: ec_domain_t *domain, /**< EtherCAT domain. */ fp@809: uint32_t logical_offset, /**< Logical offset. */ fp@809: size_t data_size, /**< Size of the data. */ fp@817: uint8_t *data, /**< Process data. */ fp@2101: const unsigned int used[] /**< Slave config counter for in/out. */ fp@635: ) fp@325: { fp@2589: ec_datagram_pair_t *datagram_pair; fp@1313: int ret; fp@293: fp@2589: if (!(datagram_pair = kmalloc(sizeof(ec_datagram_pair_t), GFP_KERNEL))) { fp@1921: EC_MASTER_ERR(domain->master, fp@2589: "Failed to allocate domain datagram pair!\n"); fp@1313: return -ENOMEM; fp@144: } fp@144: fp@2589: ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data, fp@2589: data_size, used); fp@2589: if (ret) { fp@2589: kfree(datagram_pair); fp@2589: return ret; fp@2589: } fp@2589: fp@2589: domain->expected_working_counter += fp@2589: datagram_pair->expected_working_counter; fp@2589: fp@2589: EC_MASTER_DBG(domain->master, 1, fp@2589: "Adding datagram pair with expected WC %u.\n", fp@2589: datagram_pair->expected_working_counter); fp@2589: fp@2589: fp@2589: list_add_tail(&datagram_pair->list, &domain->datagram_pairs); fp@144: return 0; fp@144: } fp@144: fp@144: /*****************************************************************************/ fp@144: fp@2101: /** Domain finish helper function. fp@2101: * fp@2101: * Detects, if a slave configuration has already been taken into account for fp@2101: * a datagram's expected working counter calculation. fp@2101: * fp@2101: * Walks through the list of all FMMU configurations for the current datagram fp@2101: * and ends before the current datagram. fp@2589: * fp@2589: * \return Non-zero if slave connfig was already counted. fp@2101: */ dave@2612: static int shall_count( fp@2101: const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to fp@2101: search for. */ fp@2101: const ec_fmmu_config_t *first_fmmu /**< Datagram's first FMMU. */ fp@2101: ) fp@2101: { fp@2101: for (; first_fmmu != cur_fmmu; fp@2101: first_fmmu = list_entry(first_fmmu->list.next, fp@2101: ec_fmmu_config_t, list)) { fp@2101: fp@2101: if (first_fmmu->sc == cur_fmmu->sc fp@2101: && first_fmmu->dir == cur_fmmu->dir) { fp@2101: return 0; // was already counted fp@2101: } fp@2101: } fp@2101: fp@2101: return 1; fp@2101: } fp@2101: fp@2101: /*****************************************************************************/ fp@2101: dave@2612: /** Domain finish helper function. dave@2612: * dave@2612: * Known boundaries for a datagram have been identified. Scans the datagram dave@2612: * FMMU boundaries for WKC counters and then creates the datagram_pair. dave@2612: * dave@2612: * \param domain The parent domain dave@2612: * \param datagram_begin_offset Datagram's logical beginning offset dave@2612: * \param datagram_end_offset Logical end offset (one past last byte) dave@2612: * \param datagram_first_fmmu The begin FMMU in the datagram dave@2612: * \param datagram_end_fmmu The end (one past last) FMMU dave@2612: * dave@2612: * \return Non-zero if error emplacing domain dave@2612: */ dave@2612: static int emplace_datagram(ec_domain_t *domain, dave@2612: uint32_t datagram_begin_offset, dave@2612: uint32_t datagram_end_offset, dave@2612: const ec_fmmu_config_t *datagram_first_fmmu, dave@2612: const ec_fmmu_config_t *datagram_end_fmmu dave@2612: ) dave@2612: { dave@2612: unsigned int datagram_used[EC_DIR_COUNT]; dave@2612: const ec_fmmu_config_t *curr_fmmu; dave@2612: size_t data_size; dave@2612: dave@2612: data_size = datagram_end_offset - datagram_begin_offset; dave@2612: dave@2612: memset(datagram_used, 0, sizeof(datagram_used)); dave@2612: for (curr_fmmu = datagram_first_fmmu; dave@2612: &curr_fmmu->list != &datagram_end_fmmu->list; dave@2612: curr_fmmu = list_next_entry(curr_fmmu, list)) { dave@2612: if (shall_count(curr_fmmu, datagram_first_fmmu)) { dave@2612: datagram_used[curr_fmmu->dir]++; dave@2612: } dave@2612: } dave@2612: dave@2612: return ec_domain_add_datagram_pair(domain, dave@2612: domain->logical_base_address + datagram_begin_offset, dave@2612: data_size, dave@2612: domain->data + datagram_begin_offset, dave@2612: datagram_used); dave@2612: } dave@2612: dave@2612: /*****************************************************************************/ dave@2612: fp@792: /** Finishes a domain. fp@792: * fp@792: * This allocates the necessary datagrams and writes the correct logical fp@792: * addresses to every configured FMMU. fp@792: * fp@1313: * \retval 0 Success fp@1313: * \retval <0 Error code. fp@792: */ fp@792: int ec_domain_finish( fp@792: ec_domain_t *domain, /**< EtherCAT domain. */ fp@792: uint32_t base_address /**< Logical base address. */ fp@792: ) fp@792: { dave@2612: uint32_t datagram_offset = 0; dave@2612: unsigned int datagram_count = 0; fp@792: ec_fmmu_config_t *fmmu; fp@2101: const ec_fmmu_config_t *datagram_first_fmmu = NULL; dave@2612: const ec_fmmu_config_t *valid_fmmu = NULL; dave@2612: unsigned candidate_start = 0; dave@2612: unsigned valid_start = 0; fp@2589: const ec_datagram_pair_t *datagram_pair; fp@1313: int ret; fp@73: fp@809: domain->logical_base_address = base_address; fp@809: fp@809: if (domain->data_size && domain->data_origin == EC_ORIG_INTERNAL) { fp@809: if (!(domain->data = fp@809: (uint8_t *) kmalloc(domain->data_size, GFP_KERNEL))) { fp@1921: EC_MASTER_ERR(domain->master, "Failed to allocate %zu bytes" fp@1921: " internal memory for domain %u!\n", fp@1921: domain->data_size, domain->index); fp@1313: return -ENOMEM; fp@809: } fp@809: } fp@809: fp@2101: // Cycle through all domain FMMUs and fp@985: // - correct the logical base addresses fp@985: // - set up the datagrams to carry the process data fp@2101: // - calculate the datagrams' expected working counters fp@2101: if (!list_empty(&domain->fmmu_configs)) { fp@2101: datagram_first_fmmu = fp@2101: list_entry(domain->fmmu_configs.next, ec_fmmu_config_t, list); fp@1907: } fp@1907: fp@985: list_for_each_entry(fmmu, &domain->fmmu_configs, list) { dave@2612: if (fmmu->data_size > EC_MAX_DATA_SIZE) { dave@2612: EC_MASTER_ERR(domain->master, dave@2612: "FMMU size %u bytes exceeds maximum data size %u", dave@2612: fmmu->data_size, EC_MAX_DATA_SIZE); dave@2612: return -EINVAL; dave@2612: } dave@2612: if (fmmu->logical_domain_offset >= candidate_start) { dave@2612: // As FMMU offsets increase monotonically, and candidate start dave@2612: // offset has never been contradicted, it can now never be dave@2612: // contradicted, as no future FMMU can cross it. dave@2614: // All FMMUs prior to this point approved for next datagram dave@2614: valid_fmmu = fmmu; dave@2614: valid_start = candidate_start; dave@2614: if (fmmu->logical_domain_offset + fmmu->data_size - datagram_offset dave@2614: > EC_MAX_DATA_SIZE) { dave@2612: // yet the new candidate exceeds the datagram size, so we dave@2612: // use the last known valid candidate to create the datagram dave@2612: ret = emplace_datagram(domain, datagram_offset, valid_start, dave@2612: datagram_first_fmmu, valid_fmmu); dave@2612: if (ret < 0) dave@2612: return ret; dave@2612: dave@2612: datagram_offset = valid_start; dave@2612: datagram_count++; dave@2612: datagram_first_fmmu = fmmu; dave@2612: } dave@2612: } dave@2612: if (fmmu->logical_domain_offset + fmmu->data_size > candidate_start) { dave@2612: candidate_start = fmmu->logical_domain_offset + fmmu->data_size; dave@2612: } fp@2589: } fp@2589: fp@2589: /* Allocate last datagram pair, if data are left (this is also the case if fp@2589: * the process data fit into a single datagram) */ dave@2612: if (domain->data_size > datagram_offset) { dave@2612: ret = emplace_datagram(domain, datagram_offset, domain->data_size, dave@2612: datagram_first_fmmu, fmmu); fp@1313: if (ret < 0) fp@1313: return ret; fp@325: datagram_count++; fp@325: } fp@325: fp@1921: EC_MASTER_INFO(domain->master, "Domain%u: Logical address 0x%08x," fp@1921: " %zu byte, expected working counter %u.\n", domain->index, fp@985: domain->logical_base_address, domain->data_size, fp@985: domain->expected_working_counter); fp@2589: fp@2589: list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) { fp@2589: const ec_datagram_t *datagram = fp@2589: &datagram_pair->datagrams[EC_DEVICE_MAIN]; fp@1921: EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x," dave@2614: " %zu byte, type %s at %p.\n", datagram->name, fp@1921: EC_READ_U32(datagram->address), datagram->data_size, dave@2614: ec_datagram_type_string(datagram), datagram); fp@817: } fp@2589: fp@325: return 0; fp@325: } fp@325: fp@325: /*****************************************************************************/ fp@325: fp@1092: /** Get the number of FMMU configurations of the domain. fp@1092: */ fp@950: unsigned int ec_domain_fmmu_count(const ec_domain_t *domain) fp@950: { fp@950: const ec_fmmu_config_t *fmmu; fp@950: unsigned int num = 0; fp@950: fp@950: list_for_each_entry(fmmu, &domain->fmmu_configs, list) { fp@950: num++; fp@950: } fp@950: fp@950: return num; fp@950: } fp@950: fp@950: /*****************************************************************************/ fp@950: fp@1092: /** Get a certain FMMU configuration via its position in the list. fp@2589: * fp@2589: * \return FMMU at position \a pos, or NULL. fp@1092: */ fp@950: const ec_fmmu_config_t *ec_domain_find_fmmu( fp@1092: const ec_domain_t *domain, /**< EtherCAT domain. */ fp@1092: unsigned int pos /**< List position. */ fp@950: ) fp@950: { fp@950: const ec_fmmu_config_t *fmmu; fp@950: fp@950: list_for_each_entry(fmmu, &domain->fmmu_configs, list) { fp@1092: if (pos--) fp@950: continue; fp@950: return fmmu; fp@950: } fp@950: fp@950: return NULL; fp@950: } fp@950: fp@2589: /*****************************************************************************/ fp@2589: fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: fp@2589: /** Process received data. fp@2589: */ fp@2589: int data_changed( fp@2589: uint8_t *send_buffer, fp@2589: const ec_datagram_t *datagram, fp@2589: size_t offset, fp@2589: size_t size fp@2589: ) fp@2589: { fp@2589: uint8_t *sent = send_buffer + offset; fp@2589: uint8_t *recv = datagram->data + offset; fp@2589: size_t i; fp@2589: fp@2589: for (i = 0; i < size; i++) { fp@2589: if (recv[i] != sent[i]) { fp@2589: return 1; fp@2589: } fp@2589: } fp@2589: fp@2589: return 0; fp@2589: } fp@2589: fp@2589: #endif fp@2589: fp@73: /****************************************************************************** fp@2101: * Application interface fp@73: *****************************************************************************/ fp@73: fp@792: int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain, fp@792: const ec_pdo_entry_reg_t *regs) fp@792: { fp@792: const ec_pdo_entry_reg_t *reg; fp@792: ec_slave_config_t *sc; fp@792: int ret; fp@2589: fp@1921: EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list(" fp@1921: "domain = 0x%p, regs = 0x%p)\n", domain, regs); fp@1181: fp@792: for (reg = regs; reg->index; reg++) { fp@1313: sc = ecrt_master_slave_config_err(domain->master, reg->alias, fp@1313: reg->position, reg->vendor_id, reg->product_code); fp@1313: if (IS_ERR(sc)) fp@1313: return PTR_ERR(sc); fp@1313: fp@1313: ret = ecrt_slave_config_reg_pdo_entry(sc, reg->index, fp@1313: reg->subindex, domain, reg->bit_position); fp@1313: if (ret < 0) fp@1313: return ret; fp@916: fp@916: *reg->offset = ret; fp@916: } fp@916: fp@916: return 0; fp@916: } fp@916: fp@916: /*****************************************************************************/ fp@916: fp@1257: size_t ecrt_domain_size(const ec_domain_t *domain) fp@809: { fp@809: return domain->data_size; fp@809: } fp@809: fp@809: /*****************************************************************************/ fp@809: fp@809: void ecrt_domain_external_memory(ec_domain_t *domain, uint8_t *mem) fp@809: { fp@1921: EC_MASTER_DBG(domain->master, 1, "ecrt_domain_external_memory(" fp@1921: "domain = 0x%p, mem = 0x%p)\n", domain, mem); fp@1181: fp@2589: down(&domain->master->master_sem); fp@1075: fp@809: ec_domain_clear_data(domain); fp@809: fp@809: domain->data = mem; fp@809: domain->data_origin = EC_ORIG_EXTERNAL; fp@1075: fp@2589: up(&domain->master->master_sem); fp@809: } fp@809: fp@809: /*****************************************************************************/ fp@809: fp@809: uint8_t *ecrt_domain_data(ec_domain_t *domain) fp@809: { fp@809: return domain->data; fp@809: } fp@809: fp@809: /*****************************************************************************/ fp@809: fp@792: void ecrt_domain_process(ec_domain_t *domain) fp@98: { fp@2589: uint16_t wc_sum[EC_MAX_NUM_DEVICES] = {}, wc_total; fp@2589: ec_datagram_pair_t *pair; fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: uint16_t datagram_pair_wc, redundant_wc; fp@2589: unsigned int datagram_offset; fp@2589: ec_fmmu_config_t *fmmu = list_first_entry(&domain->fmmu_configs, fp@2589: ec_fmmu_config_t, list); fp@2589: unsigned int redundancy; fp@2589: #endif fp@2589: unsigned int dev_idx; fp@2589: #ifdef EC_RT_SYSLOG fp@2589: unsigned int wc_change; fp@2589: #endif fp@2589: fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index); fp@2589: #endif fp@2589: fp@2589: list_for_each_entry(pair, &domain->datagram_pairs, list) { fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum); fp@2589: #else fp@2589: ec_datagram_pair_process(pair, wc_sum); fp@2589: #endif fp@2589: fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: if (ec_master_num_devices(domain->master) > 1) { fp@2589: ec_datagram_t *main_datagram = &pair->datagrams[EC_DEVICE_MAIN]; fp@2589: uint32_t logical_datagram_address = fp@2589: EC_READ_U32(main_datagram->address); fp@2589: size_t datagram_size = main_datagram->data_size; fp@2589: fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n", fp@2589: main_datagram->name, logical_datagram_address); fp@2589: #endif fp@2589: fp@2589: /* Redundancy: Go through FMMU configs to detect data changes. */ fp@2589: list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) { fp@2589: ec_datagram_t *backup_datagram = fp@2589: &pair->datagrams[EC_DEVICE_BACKUP]; fp@2589: fp@2589: if (fmmu->dir != EC_DIR_INPUT) { fp@2589: continue; fp@2589: } fp@2589: dave@2610: if (fmmu->logical_domain_offset >= datagram_size) { fp@2589: // fmmu data contained in next datagram pair fp@2589: break; fp@2589: } fp@2589: dave@2610: datagram_offset = fmmu->logical_domain_offset; fp@2589: fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, dave@2610: "input fmmu log_off=%u size=%u offset=%u\n", dave@2610: fmmu->logical_domain_offset, fmmu->data_size, fp@2589: datagram_offset); fp@2589: if (domain->master->debug_level > 0) { fp@2589: ec_print_data(pair->send_buffer + datagram_offset, fp@2589: fmmu->data_size); fp@2589: ec_print_data(main_datagram->data + datagram_offset, fp@2589: fmmu->data_size); fp@2589: ec_print_data(backup_datagram->data + datagram_offset, fp@2589: fmmu->data_size); fp@2589: } fp@2589: #endif fp@2589: fp@2589: if (data_changed(pair->send_buffer, main_datagram, fp@2589: datagram_offset, fmmu->data_size)) { fp@2589: /* data changed on main link: no copying necessary. */ fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, "main changed\n"); fp@2589: #endif fp@2589: } else if (data_changed(pair->send_buffer, backup_datagram, fp@2589: datagram_offset, fmmu->data_size)) { fp@2589: /* data changed on backup link: copy to main memory. */ fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, "backup changed\n"); fp@2589: #endif fp@2589: memcpy(main_datagram->data + datagram_offset, fp@2589: backup_datagram->data + datagram_offset, fp@2589: fmmu->data_size); fp@2589: } else if (datagram_pair_wc == fp@2589: pair->expected_working_counter) { fp@2589: /* no change, but WC complete: use main data. */ fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, fp@2589: "no change but complete\n"); fp@2589: #endif fp@2589: } else { fp@2589: /* no change and WC incomplete: mark WC as zero to avoid fp@2589: * data.dependent WC flickering. */ fp@2589: datagram_pair_wc = 0; fp@2589: #if DEBUG_REDUNDANCY fp@2589: EC_MASTER_DBG(domain->master, 1, fp@2589: "no change and incomplete\n"); fp@2589: #endif fp@2589: } fp@2589: } fp@2589: } fp@2589: #endif // EC_MAX_NUM_DEVICES > 1 fp@2589: } fp@2589: fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: redundant_wc = 0; fp@2589: for (dev_idx = EC_DEVICE_BACKUP; fp@2589: dev_idx < ec_master_num_devices(domain->master); dev_idx++) { fp@2589: redundant_wc += wc_sum[dev_idx]; fp@2589: } fp@2589: fp@2589: redundancy = redundant_wc > 0; fp@2589: if (redundancy != domain->redundancy_active) { fp@2589: #ifdef EC_RT_SYSLOG fp@2589: if (redundancy) { fp@2589: EC_MASTER_WARN(domain->master, fp@2589: "Domain %u: Redundant link in use!\n", fp@2589: domain->index); fp@2589: } else { fp@2589: EC_MASTER_INFO(domain->master, fp@2589: "Domain %u: Redundant link unused again.\n", fp@2589: domain->index); fp@2589: } fp@2589: #endif fp@2589: domain->redundancy_active = redundancy; fp@2589: } fp@2589: #else fp@2589: domain->redundancy_active = 0; fp@2589: #endif fp@2589: fp@2589: #ifdef EC_RT_SYSLOG fp@2589: wc_change = 0; fp@2589: #endif fp@2589: wc_total = 0; fp@2589: for (dev_idx = EC_DEVICE_MAIN; fp@2589: dev_idx < ec_master_num_devices(domain->master); dev_idx++) { fp@2589: if (wc_sum[dev_idx] != domain->working_counter[dev_idx]) { fp@2589: #ifdef EC_RT_SYSLOG fp@2589: wc_change = 1; fp@2589: #endif fp@2589: domain->working_counter[dev_idx] = wc_sum[dev_idx]; fp@2589: } fp@2589: wc_total += wc_sum[dev_idx]; fp@2589: } fp@2589: fp@2589: #ifdef EC_RT_SYSLOG fp@2589: if (wc_change) { fp@332: domain->working_counter_changes++; fp@332: } fp@332: fp@332: if (domain->working_counter_changes && fp@344: jiffies - domain->notify_jiffies > HZ) { fp@344: domain->notify_jiffies = jiffies; fp@332: if (domain->working_counter_changes == 1) { fp@1921: EC_MASTER_INFO(domain->master, "Domain %u: Working counter" fp@2589: " changed to %u/%u", domain->index, fp@2589: wc_total, domain->expected_working_counter); fp@1921: } else { fp@1921: EC_MASTER_INFO(domain->master, "Domain %u: %u working counter" fp@2589: " changes - now %u/%u", domain->index, fp@2589: domain->working_counter_changes, fp@2589: wc_total, domain->expected_working_counter); fp@2589: } fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: if (ec_master_num_devices(domain->master) > 1) { fp@2589: printk(" ("); fp@2589: for (dev_idx = EC_DEVICE_MAIN; fp@2589: dev_idx < ec_master_num_devices(domain->master); fp@2589: dev_idx++) { fp@2589: printk("%u", domain->working_counter[dev_idx]); fp@2589: if (dev_idx + 1 < ec_master_num_devices(domain->master)) { fp@2589: printk("+"); fp@2589: } fp@2589: } fp@2589: printk(")"); fp@2589: } fp@2589: #endif fp@2589: printk(".\n"); fp@2589: fp@332: domain->working_counter_changes = 0; fp@325: } fp@2589: #endif fp@494: } fp@494: fp@494: /*****************************************************************************/ fp@494: fp@792: void ecrt_domain_queue(ec_domain_t *domain) fp@494: { fp@2589: ec_datagram_pair_t *datagram_pair; fp@2589: ec_device_index_t dev_idx; fp@2589: fp@2589: list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) { fp@2589: fp@2589: #if EC_MAX_NUM_DEVICES > 1 fp@2589: /* copy main data to send buffer */ fp@2589: memcpy(datagram_pair->send_buffer, fp@2589: datagram_pair->datagrams[EC_DEVICE_MAIN].data, fp@2589: datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); fp@2589: #endif fp@2589: ec_master_queue_datagram(domain->master, fp@2589: &datagram_pair->datagrams[EC_DEVICE_MAIN]); fp@2589: fp@2589: /* copy main data to backup datagram */ fp@2589: for (dev_idx = EC_DEVICE_BACKUP; fp@2589: dev_idx < ec_master_num_devices(domain->master); dev_idx++) { fp@2589: memcpy(datagram_pair->datagrams[dev_idx].data, fp@2589: datagram_pair->datagrams[EC_DEVICE_MAIN].data, fp@2589: datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); fp@2589: ec_master_queue_datagram(domain->master, fp@2589: &datagram_pair->datagrams[dev_idx]); fp@2589: } fp@494: } fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@792: void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state) fp@792: { fp@2589: unsigned int dev_idx; fp@2589: uint16_t wc = 0; fp@2589: fp@2589: for (dev_idx = EC_DEVICE_MAIN; fp@2589: dev_idx < ec_master_num_devices(domain->master); dev_idx++) { fp@2589: wc += domain->working_counter[dev_idx]; fp@2589: } fp@2589: fp@2589: state->working_counter = wc; fp@2589: fp@2589: if (wc) { fp@2589: if (wc == domain->expected_working_counter) { fp@818: state->wc_state = EC_WC_COMPLETE; fp@818: } else { fp@818: state->wc_state = EC_WC_INCOMPLETE; fp@818: } fp@818: } else { fp@818: state->wc_state = EC_WC_ZERO; fp@818: } fp@2589: fp@2589: state->redundancy_active = domain->redundancy_active; fp@105: } fp@105: fp@105: /*****************************************************************************/ fp@105: fp@199: /** \cond */ fp@199: fp@792: EXPORT_SYMBOL(ecrt_domain_reg_pdo_entry_list); fp@809: EXPORT_SYMBOL(ecrt_domain_size); fp@809: EXPORT_SYMBOL(ecrt_domain_external_memory); fp@809: EXPORT_SYMBOL(ecrt_domain_data); fp@104: EXPORT_SYMBOL(ecrt_domain_process); fp@494: EXPORT_SYMBOL(ecrt_domain_queue); fp@105: EXPORT_SYMBOL(ecrt_domain_state); fp@42: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/