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@2269: #include "datagram_pair.h" fp@325: fp@2367: #define DEBUG_REDUNDANCY 0 fp@2367: 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@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@2269: INIT_LIST_HEAD(&domain->datagram_pairs); fp@1032: domain->working_counter = 0x0000; fp@985: domain->expected_working_counter = 0x0000; fp@818: domain->working_counter_changes = 0; fp@344: domain->notify_jiffies = 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@2269: ec_datagram_pair_t *datagram_pair, *next_pair; fp@993: fp@993: // dequeue and free datagrams fp@2269: list_for_each_entry_safe(datagram_pair, next_pair, fp@2269: &domain->datagram_pairs, list) { fp@2269: ec_datagram_pair_clear(datagram_pair); fp@2269: 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@2269: if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) { fp@809: kfree(domain->data); fp@2269: } fp@2269: 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: { fp@818: fmmu->domain = domain; fp@818: fp@818: domain->data_size += fmmu->data_size; fp@950: list_add_tail(&fmmu->list, &domain->fmmu_configs); fp@912: fp@1921: EC_MASTER_DBG(domain->master, 1, "Domain %u:" fp@1921: " Added %u bytes, total %zu.\n", fp@1921: domain->index, fmmu->data_size, domain->data_size); fp@818: } fp@818: fp@818: /*****************************************************************************/ fp@818: fp@2269: /** Allocates a domain datagram pair and appends it to the list. fp@2269: * fp@2269: * The datagrams' types and expected working counters are determined by the fp@2269: * 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@2269: 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@2366: const unsigned int used[] /**< Slave config counter for in/out. */ fp@635: ) fp@325: { fp@2269: ec_datagram_pair_t *datagram_pair; fp@1313: int ret; fp@2269: fp@2269: if (!(datagram_pair = kmalloc(sizeof(ec_datagram_pair_t), GFP_KERNEL))) { fp@1921: EC_MASTER_ERR(domain->master, fp@2269: "Failed to allocate domain datagram pair!\n"); fp@1313: return -ENOMEM; fp@144: } fp@144: fp@2367: ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data, fp@2367: data_size, used); fp@2269: if (ret) { fp@2269: kfree(datagram_pair); fp@2269: return ret; fp@2269: } fp@2269: fp@2367: domain->expected_working_counter += fp@2367: datagram_pair->expected_working_counter; fp@2367: fp@2367: EC_MASTER_DBG(domain->master, 1, fp@2367: "Adding datagram pair with expected WC %u.\n", fp@2367: datagram_pair->expected_working_counter); fp@2367: fp@2269: fp@2269: list_add_tail(&datagram_pair->list, &domain->datagram_pairs); fp@144: return 0; fp@144: } fp@144: fp@144: /*****************************************************************************/ fp@144: fp@2366: /** Domain finish helper function. fp@2366: * fp@2366: * Detects, if a slave configuration has already been taken into account for fp@2366: * a datagram's expected working counter calculation. fp@2366: * fp@2366: * Walks through the list of all FMMU configurations for the current datagram fp@2366: * and ends before the current datagram. fp@2366: */ fp@2366: int shall_count( fp@2366: const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to fp@2366: search for. */ fp@2366: const ec_fmmu_config_t *first_fmmu /**< Datagram's first FMMU. */ fp@2366: ) fp@2366: { fp@2366: for (; first_fmmu != cur_fmmu; fp@2366: first_fmmu = list_entry(first_fmmu->list.next, fp@2366: ec_fmmu_config_t, list)) { fp@2366: fp@2366: if (first_fmmu->sc == cur_fmmu->sc fp@2366: && first_fmmu->dir == cur_fmmu->dir) { fp@2366: return 0; // was already counted fp@2366: } fp@2366: } fp@2366: fp@2366: return 1; fp@2366: } fp@2366: fp@2366: /*****************************************************************************/ fp@2366: 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@809: * \todo Check for FMMUs that do not fit into any datagram. fp@809: * 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: { fp@792: uint32_t datagram_offset; fp@809: size_t datagram_size; fp@985: unsigned int datagram_count; fp@1055: unsigned int datagram_used[EC_DIR_COUNT]; fp@792: ec_fmmu_config_t *fmmu; fp@2366: const ec_fmmu_config_t *datagram_first_fmmu = NULL; fp@2269: 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@2366: // 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@2366: // - calculate the datagrams' expected working counters fp@809: datagram_offset = 0; fp@809: datagram_size = 0; fp@325: datagram_count = 0; fp@817: datagram_used[EC_DIR_OUTPUT] = 0; fp@817: datagram_used[EC_DIR_INPUT] = 0; fp@1907: fp@2366: if (!list_empty(&domain->fmmu_configs)) { fp@2366: datagram_first_fmmu = fp@2366: 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) { fp@2366: fp@985: // Correct logical FMMU address fp@985: fmmu->logical_start_address += base_address; fp@985: fp@985: // Increment Input/Output counter to determine datagram types fp@985: // and calculate expected working counters fp@2366: if (shall_count(fmmu, datagram_first_fmmu)) { ch1010252@1798: datagram_used[fmmu->dir]++; ch1010252@1798: } fp@1907: fp@985: // If the current FMMU's data do not fit in the current datagram, fp@985: // allocate a new one. fp@985: if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) { fp@2269: ret = ec_domain_add_datagram_pair(domain, fp@1313: domain->logical_base_address + datagram_offset, fp@1313: datagram_size, domain->data + datagram_offset, fp@1313: datagram_used); fp@1313: if (ret < 0) fp@1313: return ret; fp@2366: fp@985: datagram_offset += datagram_size; fp@985: datagram_size = 0; fp@985: datagram_count++; fp@985: datagram_used[EC_DIR_OUTPUT] = 0; fp@985: datagram_used[EC_DIR_INPUT] = 0; fp@2366: datagram_first_fmmu = fmmu; fp@985: } fp@985: fp@985: datagram_size += fmmu->data_size; fp@809: } fp@809: fp@2269: /* Allocate last datagram pair, if data are left (this is also the case if fp@2269: * the process data fit into a single datagram) */ fp@809: if (datagram_size) { fp@2269: ret = ec_domain_add_datagram_pair(domain, fp@1313: domain->logical_base_address + datagram_offset, fp@1313: datagram_size, domain->data + datagram_offset, fp@1313: datagram_used); 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@2269: fp@2269: list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) { fp@2269: const ec_datagram_t *datagram = fp@2269: &datagram_pair->datagrams[EC_DEVICE_MAIN]; fp@1921: EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x," fp@1921: " %zu byte, type %s.\n", datagram->name, fp@1921: EC_READ_U32(datagram->address), datagram->data_size, fp@1921: ec_datagram_type_string(datagram)); fp@817: } fp@2269: 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@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@73: /****************************************************************************** fp@2269: * 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@2269: 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@1075: 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@1075: 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@985: uint16_t working_counter_sum; fp@2367: ec_datagram_pair_t *datagram_pair = NULL; fp@2367: ec_fmmu_config_t *fmmu; fp@2367: uint32_t logical_datagram_address; fp@2367: unsigned int datagram_offset, datagram_pair_wc = 0; fp@2367: size_t datagram_size; fp@2367: ec_datagram_t *main_datagram; fp@2367: fp@2367: #if DEBUG_REDUNDANCY fp@2367: EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index); fp@2367: #endif fp@2269: fp@2269: working_counter_sum = 0; fp@2367: fp@2367: if (!list_empty(&domain->datagram_pairs)) { fp@2367: datagram_pair = fp@2367: list_entry(domain->datagram_pairs.next, ec_datagram_pair_t, list); fp@2367: main_datagram = &datagram_pair->datagrams[EC_DEVICE_MAIN]; fp@2367: fp@2367: logical_datagram_address = EC_READ_U32(main_datagram->address); fp@2367: datagram_size = main_datagram->data_size; fp@2367: datagram_offset = fp@2367: fmmu->logical_start_address - logical_datagram_address; fp@2367: datagram_pair_wc = ec_datagram_pair_process(datagram_pair); fp@2367: working_counter_sum += datagram_pair_wc; fp@2367: #if DEBUG_REDUNDANCY fp@2367: EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n", fp@2367: main_datagram->name, logical_datagram_address); fp@2367: #endif fp@2367: } fp@2367: fp@2367: /* Go through all FMMU configs to detect data changes. */ fp@2367: list_for_each_entry(fmmu, &domain->fmmu_configs, list) { fp@2367: #if DEBUG_REDUNDANCY fp@2367: EC_MASTER_DBG(domain->master, 1, "fmmu log=%u size=%u dir=%u\n", fp@2367: fmmu->logical_start_address, fmmu->data_size, fmmu->dir); fp@2367: #endif fp@2367: if (fmmu->dir != EC_DIR_INPUT) { fp@2367: continue; fp@2367: } fp@2367: fp@2367: logical_datagram_address = fp@2367: EC_READ_U32(datagram_pair->datagrams[EC_DEVICE_MAIN].address); fp@2367: datagram_size = datagram_pair->datagrams[EC_DEVICE_MAIN].data_size; fp@2367: datagram_offset = fp@2367: fmmu->logical_start_address - logical_datagram_address; fp@2367: while (datagram_offset >= datagram_size) { fp@2367: fp@2367: datagram_pair = list_entry(datagram_pair->list.next, fp@2367: ec_datagram_pair_t, list); fp@2367: main_datagram = &datagram_pair->datagrams[EC_DEVICE_MAIN]; fp@2367: fp@2367: logical_datagram_address = EC_READ_U32(main_datagram->address); fp@2367: datagram_size = main_datagram->data_size; fp@2367: datagram_offset = fp@2367: fmmu->logical_start_address - logical_datagram_address; fp@2367: datagram_pair_wc = ec_datagram_pair_process(datagram_pair); fp@2367: working_counter_sum += datagram_pair_wc; fp@2367: #if DEBUG_REDUNDANCY fp@2367: EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n", fp@2367: main_datagram->name, logical_datagram_address); fp@2367: #endif fp@2367: } fp@2367: fp@2367: #if DEBUG_REDUNDANCY fp@2367: EC_MASTER_DBG(domain->master, 1, "input fmmu log=%u size=%u" fp@2367: " using datagram %s offset=%u\n", fp@2367: fmmu->logical_start_address, fmmu->data_size, fp@2367: datagram_pair->datagrams[EC_DEVICE_MAIN].name, fp@2367: datagram_offset); fp@2367: #endif fp@2367: fp@2367: if (ec_datagram_pair_data_changed(datagram_pair, fp@2367: datagram_offset, fmmu->data_size, EC_DEVICE_MAIN)) { fp@2367: /* data changed on main link. no copying necessary. */ fp@2367: } else if (ec_datagram_pair_data_changed(datagram_pair, fp@2367: datagram_offset, fmmu->data_size, EC_DEVICE_BACKUP) fp@2367: || (datagram_pair_wc fp@2367: == datagram_pair->expected_working_counter)) { fp@2367: /* data changed on backup link or no change and complete WC. fp@2367: * copy to main memory. */ fp@2367: uint8_t *target = datagram_pair->datagrams[EC_DEVICE_MAIN].data + fp@2367: datagram_offset; fp@2367: uint8_t *source = datagram_pair->datagrams[EC_DEVICE_BACKUP].data + fp@2367: datagram_offset; fp@2367: memcpy(target, source, fmmu->data_size); fp@98: } fp@98: } fp@73: fp@792: if (working_counter_sum != domain->working_counter) { fp@332: domain->working_counter_changes++; fp@792: domain->working_counter = working_counter_sum; 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@1921: " changed to %u/%u.\n", domain->index, fp@2365: domain->working_counter, fp@2365: domain->expected_working_counter); fp@1921: } else { fp@1921: EC_MASTER_INFO(domain->master, "Domain %u: %u working counter" fp@1921: " changes - now %u/%u.\n", domain->index, fp@1921: domain->working_counter_changes, domain->working_counter, fp@912: domain->expected_working_counter); fp@332: } fp@332: domain->working_counter_changes = 0; fp@325: } fp@494: } fp@494: fp@494: /*****************************************************************************/ fp@494: fp@792: void ecrt_domain_queue(ec_domain_t *domain) fp@494: { fp@2269: ec_datagram_pair_t *datagram_pair; fp@2365: unsigned int dev_idx; fp@2269: fp@2269: list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) { fp@2269: fp@2367: /* copy main data to send buffer */ fp@2367: memcpy(datagram_pair->send_buffer, fp@2367: datagram_pair->datagrams[EC_DEVICE_MAIN].data, fp@2367: datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); fp@2367: fp@2269: /* copy main data to backup datagram */ fp@2269: memcpy(datagram_pair->datagrams[EC_DEVICE_BACKUP].data, fp@2269: datagram_pair->datagrams[EC_DEVICE_MAIN].data, fp@2269: datagram_pair->datagrams[EC_DEVICE_MAIN].data_size); fp@2269: fp@2365: for (dev_idx = 0; dev_idx < EC_NUM_DEVICES; dev_idx++) { fp@2269: ec_master_queue_datagram(domain->master, fp@2365: &datagram_pair->datagrams[dev_idx], dev_idx); fp@2269: } 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@792: state->working_counter = domain->working_counter; fp@818: fp@818: if (domain->working_counter) { fp@818: if (domain->working_counter == 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@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: /*****************************************************************************/