fp@42: /****************************************************************************** fp@42: * fp@42: * $Id$ fp@42: * fp@197: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@197: * fp@197: * This file is part of the IgH EtherCAT Master. fp@197: * fp@197: * The IgH EtherCAT Master is free software; you can redistribute it fp@197: * and/or modify it under the terms of the GNU General Public License fp@246: * as published by the Free Software Foundation; either version 2 of the fp@246: * License, or (at your option) any later version. fp@197: * fp@197: * The IgH EtherCAT Master is distributed in the hope that it will be fp@197: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@197: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@197: * GNU General Public License for more details. fp@197: * fp@197: * You should have received a copy of the GNU General Public License fp@197: * along 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@246: * The right to use EtherCAT Technology is granted and comes free of fp@246: * charge under condition of compatibility of product made by fp@246: * Licensee. People intending to distribute/sell products based on the fp@246: * code, have to sign an agreement to guarantee that products using fp@246: * software based on IgH EtherCAT master stay compatible with the actual fp@246: * EtherCAT specification (which are released themselves as an open fp@246: * standard) as the (only) precondition to have the right to use EtherCAT fp@246: * Technology, IP and trade marks. 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@404: void ec_domain_clear(struct kobject *); fp@809: void ec_domain_clear_data(ec_domain_t *); fp@179: ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *); fp@98: fp@42: /*****************************************************************************/ fp@42: fp@818: /** Working counter increment values for logical read/write operations. fp@818: * fp@818: * \attention This is indexed by ec_direction_t. fp@818: */ fp@818: static const unsigned int working_counter_increment[] = {2, 1}; fp@818: fp@818: /*****************************************************************************/ fp@818: fp@199: /** \cond */ fp@199: fp@315: EC_SYSFS_READ_ATTR(image_size); fp@178: fp@178: static struct attribute *def_attrs[] = { fp@315: &attr_image_size, fp@178: NULL, fp@178: }; fp@178: fp@178: static struct sysfs_ops sysfs_ops = { fp@178: .show = &ec_show_domain_attribute, fp@178: .store = NULL fp@178: }; fp@178: fp@178: static struct kobj_type ktype_ec_domain = { fp@178: .release = ec_domain_clear, fp@178: .sysfs_ops = &sysfs_ops, fp@178: .default_attrs = def_attrs fp@178: }; fp@178: fp@199: /** \endcond */ fp@199: fp@178: /*****************************************************************************/ fp@178: fp@792: /** Domain constructor. fp@792: * fp@792: * \return 0 in case of success, else < 0 fp@792: */ fp@792: int 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@73: domain->data_size = 0; fp@818: domain->expected_working_counter = 0; fp@809: domain->data = NULL; fp@809: domain->data_origin = EC_ORIG_INTERNAL; fp@809: domain->logical_base_address = 0L; fp@792: domain->working_counter = 0xFFFFFFFF; fp@818: domain->working_counter_changes = 0; fp@344: domain->notify_jiffies = 0; fp@73: fp@293: INIT_LIST_HEAD(&domain->datagrams); fp@178: fp@195: // init kobject and add it to the hierarchy fp@178: memset(&domain->kobj, 0x00, sizeof(struct kobject)); fp@178: kobject_init(&domain->kobj); fp@178: domain->kobj.ktype = &ktype_ec_domain; fp@178: domain->kobj.parent = &master->kobj; fp@783: if (kobject_set_name(&domain->kobj, "domain%u", index)) { fp@178: EC_ERR("Failed to set kobj name.\n"); fp@484: kobject_put(&domain->kobj); fp@484: return -1; fp@484: } fp@484: if (kobject_add(&domain->kobj)) { fp@484: EC_ERR("Failed to add domain kobject.\n"); fp@484: kobject_put(&domain->kobj); fp@178: return -1; fp@178: } fp@178: fp@178: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@818: /** Domain destructor. fp@818: * fp@818: * Clears and frees a domain object. fp@818: */ fp@448: void ec_domain_destroy(ec_domain_t *domain /**< EtherCAT domain */) fp@448: { fp@448: ec_datagram_t *datagram; fp@448: fp@448: // dequeue datagrams fp@448: list_for_each_entry(datagram, &domain->datagrams, list) { fp@448: if (!list_empty(&datagram->queue)) // datagram queued? fp@448: list_del_init(&datagram->queue); fp@448: } fp@448: fp@448: // destroy self fp@448: kobject_del(&domain->kobj); fp@448: kobject_put(&domain->kobj); fp@448: } fp@448: fp@448: /*****************************************************************************/ fp@448: fp@818: /** Clear and free domain. fp@818: * fp@818: * This method is called by the kobject, once there are no more references fp@818: * to it. fp@818: */ fp@195: void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */) fp@73: { fp@792: ec_domain_t *domain; fp@293: ec_datagram_t *datagram, *next; fp@178: fp@178: domain = container_of(kobj, ec_domain_t, kobj); fp@178: fp@293: list_for_each_entry_safe(datagram, next, &domain->datagrams, list) { fp@293: ec_datagram_clear(datagram); fp@293: kfree(datagram); fp@144: } fp@98: fp@809: ec_domain_clear_data(domain); fp@809: fp@178: kfree(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@912: unsigned int wc_increment; fp@818: fmmu->domain = domain; fp@818: fp@818: domain->data_size += fmmu->data_size; fp@912: wc_increment = working_counter_increment[fmmu->dir]; fp@912: domain->expected_working_counter += wc_increment; fp@912: fp@912: if (domain->master->debug_level) fp@912: EC_DBG("Domain %u: Added %u bytes (now %u) with dir %u -> WC %u" fp@912: " (now %u).\n", domain->index, fmmu->data_size, fp@912: domain->data_size, fmmu->dir, wc_increment, fp@912: domain->expected_working_counter); fp@818: } fp@818: fp@818: /*****************************************************************************/ fp@818: fp@792: /** Allocates a domain datagram and appends it to the list. fp@792: * 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@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@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@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@792: unsigned int datagram_count, i; fp@817: unsigned int datagram_used[2]; fp@792: ec_slave_config_t *sc; 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@809: // Cycle through all domain FMMUS, correct the logical base addresses and fp@809: // 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@792: list_for_each_entry(sc, &domain->master->configs, list) { fp@792: for (i = 0; i < sc->used_fmmus; i++) { fp@792: fmmu = &sc->fmmu_configs[i]; fp@792: if (fmmu->domain != domain) fp@792: continue; fp@792: fp@809: // Correct logical FMMU address fp@792: fmmu->logical_start_address += base_address; fp@809: fp@817: // Increment Input/Output counter fp@817: datagram_used[fmmu->dir]++; fp@817: fp@809: // If the current FMMU's data do not fit in the current datagram, fp@809: // allocate a new one. fp@809: if (datagram_size + fmmu->data_size > EC_MAX_DATA_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@809: return -1; fp@809: datagram_offset += datagram_size; fp@809: datagram_size = 0; fp@792: datagram_count++; fp@817: datagram_used[EC_DIR_OUTPUT] = 0; fp@817: datagram_used[EC_DIR_INPUT] = 0; fp@73: } fp@809: fp@809: datagram_size += fmmu->data_size; fp@809: } fp@809: } fp@809: fp@809: // allocate last datagram, if data are left 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@817: EC_INFO("Domain %u with logical offset %u contains %u bytes.\n", fp@817: domain->index, domain->logical_base_address, domain->data_size); fp@817: list_for_each_entry(datagram, &domain->datagrams, list) { fp@817: EC_INFO(" Datagram %s, logical offset %u, size %u, 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@325: /** fp@195: Formats attribute data for SysFS reading. fp@195: \return number of bytes to read fp@195: */ fp@195: fp@195: ssize_t ec_show_domain_attribute(struct kobject *kobj, /**< kobject */ fp@195: struct attribute *attr, /**< attribute */ fp@195: char *buffer /**< memory to store data in */ fp@178: ) fp@178: { fp@178: ec_domain_t *domain = container_of(kobj, ec_domain_t, kobj); fp@178: fp@315: if (attr == &attr_image_size) { fp@783: return sprintf(buffer, "%u\n", domain->data_size); fp@178: } fp@178: fp@178: return 0; fp@97: } fp@97: 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@792: for (reg = regs; reg->index; reg++) { fp@792: if (!(sc = ecrt_master_slave_config(domain->master, reg->alias, fp@792: 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@807: reg->subindex, domain)) < 0) fp@640: return -1; fp@792: fp@792: *reg->offset = ret; fp@792: } fp@792: fp@792: return 0; fp@792: } fp@792: fp@792: /*****************************************************************************/ fp@792: fp@916: int ecrt_domain_reg_pdo_entry_list_bitwise(ec_domain_t *domain, fp@916: const ec_pdo_entry_reg_t *regs) fp@916: { fp@916: const ec_pdo_entry_reg_t *reg; fp@916: ec_slave_config_t *sc; fp@916: int ret; fp@916: fp@916: for (reg = regs; reg->index; reg++) { fp@916: if (!(sc = ecrt_master_slave_config(domain->master, reg->alias, fp@916: reg->position, reg->vendor_id, reg->product_code))) fp@916: return -1; fp@916: fp@916: if ((ret = ecrt_slave_config_reg_pdo_entry_bitwise(sc, reg->index, fp@916: reg->subindex, domain)) < 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@809: ec_domain_clear_data(domain); fp@809: fp@809: domain->data = mem; fp@809: domain->data_origin = EC_ORIG_EXTERNAL; 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@144: unsigned int working_counter_sum; fp@293: ec_datagram_t *datagram; fp@98: fp@98: working_counter_sum = 0; 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@332: } fp@332: else { fp@912: EC_INFO("Domain %u: %u working counter changes. Currently %u/%u.\n", fp@332: domain->index, domain->working_counter_changes, fp@912: 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: /*****************************************************************************/