fp@42: /****************************************************************************** fp@42: * fp@42: * $Id$ fp@42: * fp@1685: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@1685: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1685: * modify it under the terms of the GNU General Public License version 2, as fp@1685: * published by the Free Software Foundation. fp@1685: * fp@1685: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1685: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1685: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1685: * Public License for more details. fp@1685: * fp@1685: * You should have received a copy of the GNU General Public License along fp@1685: * 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@1685: * Using the EtherCAT technology and brand is permitted in compliance with fp@1685: * the 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@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@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@985: INIT_LIST_HEAD(&domain->datagrams); 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@993: ec_datagram_t *datagram, *next; fp@993: fp@993: // dequeue and free datagrams fp@993: list_for_each_entry_safe(datagram, next, &domain->datagrams, list) { fp@448: if (!list_empty(&datagram->queue)) // datagram queued? fp@993: list_del(&datagram->queue); fp@293: ec_datagram_clear(datagram); fp@293: kfree(datagram); 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@809: if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) fp@809: kfree(domain->data); 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@912: if (domain->master->debug_level) fp@985: EC_DBG("Domain %u: Added %u bytes, total %u.\n", domain->index, fp@985: fmmu->data_size, domain->data_size); fp@818: } fp@818: fp@818: /*****************************************************************************/ fp@818: fp@792: /** Allocates a domain datagram and appends it to the list. fp@792: * fp@985: * The datagram type and expected working counters are determined by the fp@985: * number of input and output fmmus that share the datagram. fp@985: * fp@635: * \return 0 in case of success, else < 0 fp@635: */ fp@792: int ec_domain_add_datagram( 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@817: const unsigned int used[] /**< Used by inputs/outputs. */ fp@635: ) fp@325: { fp@293: ec_datagram_t *datagram; fp@293: fp@293: if (!(datagram = kmalloc(sizeof(ec_datagram_t), GFP_KERNEL))) { fp@293: EC_ERR("Failed to allocate domain datagram!\n"); fp@144: return -1; fp@144: } fp@144: fp@293: ec_datagram_init(datagram); fp@719: snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE, fp@809: "domain%u-%u", domain->index, logical_offset); fp@809: fp@817: if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs fp@817: if (ec_datagram_lrw(datagram, logical_offset, data_size, data)) { fp@817: kfree(datagram); fp@817: return -1; fp@817: } fp@985: // If LRW is used, output FMMUs increment the working counter by 2, fp@985: // while input FMMUs increment it by 1. fp@1653: domain->expected_working_counter += fp@985: used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT]; fp@817: } else if (used[EC_DIR_OUTPUT]) { // outputs only fp@817: if (ec_datagram_lwr(datagram, logical_offset, data_size, data)) { fp@817: kfree(datagram); fp@817: return -1; fp@817: } fp@1653: domain->expected_working_counter += used[EC_DIR_OUTPUT]; fp@817: } else { // inputs only (or nothing) fp@817: if (ec_datagram_lrd(datagram, logical_offset, data_size, data)) { fp@817: kfree(datagram); fp@817: return -1; fp@817: } fp@1653: domain->expected_working_counter += used[EC_DIR_INPUT]; fp@144: } fp@144: fp@293: list_add_tail(&datagram->list, &domain->datagrams); fp@144: return 0; fp@144: } fp@144: fp@144: /*****************************************************************************/ fp@144: 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@792: * \retval 0 in case of success fp@792: * \retval <0 on failure. 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@817: const ec_datagram_t *datagram; 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@809: EC_ERR("Failed to allocate %u bytes internal memory for" fp@809: " domain %u!\n", domain->data_size, domain->index); fp@809: return -1; fp@809: } fp@809: } fp@809: fp@985: // 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@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@817: fp@985: list_for_each_entry(fmmu, &domain->fmmu_configs, list) { 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@985: datagram_used[fmmu->dir]++; fp@985: 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@985: if (ec_domain_add_datagram(domain, fp@985: domain->logical_base_address + datagram_offset, fp@985: datagram_size, domain->data + datagram_offset, fp@985: datagram_used)) fp@985: return -1; 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@985: } fp@985: fp@985: datagram_size += fmmu->data_size; fp@809: } fp@809: fp@993: // Allocate last datagram, if data are left (this is also the case if the fp@993: // process data fit into a single datagram) fp@809: if (datagram_size) { fp@809: if (ec_domain_add_datagram(domain, fp@809: domain->logical_base_address + datagram_offset, fp@817: datagram_size, domain->data + datagram_offset, fp@817: datagram_used)) fp@144: return -1; fp@325: datagram_count++; fp@325: } fp@325: fp@985: EC_INFO("Domain%u: Logical address 0x%08x, %u byte, " fp@985: "expected working counter %u.\n", domain->index, fp@985: domain->logical_base_address, domain->data_size, fp@985: domain->expected_working_counter); fp@817: list_for_each_entry(datagram, &domain->datagrams, list) { fp@985: EC_INFO(" Datagram %s: Logical offset 0x%08x, %u byte, type %s.\n", fp@817: datagram->name, EC_READ_U32(datagram->address), fp@817: datagram->data_size, ec_datagram_type_string(datagram)); fp@817: } fp@817: 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@195: * Realtime 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@640: fp@1181: if (domain->master->debug_level) fp@1181: EC_DBG("ecrt_domain_reg_pdo_entry_list(domain = 0x%x, regs = 0x%x)\n", fp@1181: (u32) domain, (u32) regs); fp@1181: fp@792: for (reg = regs; reg->index; reg++) { fp@792: if (!(sc = ecrt_master_slave_config(domain->master, reg->alias, fp@1010: reg->position, reg->vendor_id, reg->product_code))) fp@98: return -1; fp@98: fp@807: if ((ret = ecrt_slave_config_reg_pdo_entry(sc, reg->index, fp@925: reg->subindex, domain, reg->bit_position)) < 0) fp@916: return -1; fp@916: fp@916: *reg->offset = ret; fp@916: } fp@916: fp@916: return 0; fp@916: } fp@916: fp@916: /*****************************************************************************/ fp@916: fp@809: size_t ecrt_domain_size(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@1181: if (domain->master->debug_level) fp@1181: EC_DBG("ecrt_domain_external_memory(domain = 0x%x, mem = 0x%x)\n", fp@1181: (u32) domain, (u32) 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@293: ec_datagram_t *datagram; fp@98: fp@985: working_counter_sum = 0x0000; fp@293: list_for_each_entry(datagram, &domain->datagrams, list) { fp@719: ec_datagram_output_stats(datagram); fp@325: if (datagram->state == EC_DATAGRAM_RECEIVED) { fp@293: working_counter_sum += datagram->working_counter; 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@912: EC_INFO("Domain %u: Working counter changed to %u/%u.\n", fp@912: domain->index, domain->working_counter, fp@912: domain->expected_working_counter); fp@985: } else { fp@1116: EC_INFO("Domain %u: %u working counter changes - now %u/%u.\n", fp@1116: domain->index, domain->working_counter_changes, fp@1116: domain->working_counter, 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@494: ec_datagram_t *datagram; fp@494: fp@494: list_for_each_entry(datagram, &domain->datagrams, list) { fp@494: ec_master_queue_datagram(domain->master, datagram); 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: /*****************************************************************************/