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 <linux/module.h>
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@818:     fmmu->domain = domain;
fp@818: 
fp@818:     domain->data_size += fmmu->data_size;
fp@818:     domain->expected_working_counter += working_counter_increment[fmmu->dir];
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@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@783:             EC_INFO("Domain %u working counter change: %u\n", domain->index,
fp@792:                     domain->working_counter);
fp@332:         }
fp@332:         else {
fp@792:             EC_INFO("Domain %u: %u working counter changes. Currently %u\n",
fp@332:                     domain->index, domain->working_counter_changes,
fp@792:                     domain->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: /*****************************************************************************/