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@54: #include "domain.h"
fp@73: #include "master.h"
fp@42: 
fp@178: /*****************************************************************************/
fp@178: 
fp@325: /**
fp@325:    Data registration type.
fp@325: */
fp@325: 
fp@325: typedef struct
fp@325: {
fp@325:     struct list_head list; /**< list item */
fp@325:     ec_slave_t *slave; /**< slave */
fp@325:     const ec_sii_sync_t *sync; /**< sync manager */
fp@325:     off_t sync_offset; /**< pdo offset */
fp@325:     void **data_ptr; /**< pointer to process data pointer(s) */
fp@325: }
fp@325: ec_data_reg_t;
fp@325: 
fp@325: /*****************************************************************************/
fp@325: 
fp@404: void ec_domain_clear(struct kobject *);
fp@325: void ec_domain_clear_data_regs(ec_domain_t *);
fp@179: ssize_t ec_show_domain_attribute(struct kobject *, struct attribute *, char *);
fp@98: 
fp@42: /*****************************************************************************/
fp@42: 
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@42: /**
fp@195:    Domain constructor.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@195: int ec_domain_init(ec_domain_t *domain, /**< EtherCAT domain */
fp@195:                    ec_master_t *master, /**< owning master */
fp@195:                    unsigned int index /**< domain index */
fp@178:                    )
fp@73: {
fp@73:     domain->master = master;
fp@178:     domain->index = index;
fp@73:     domain->data_size = 0;
fp@73:     domain->base_address = 0;
fp@73:     domain->response_count = 0xFFFFFFFF;
fp@344:     domain->notify_jiffies = 0;
fp@332:     domain->working_counter_changes = 0;
fp@73: 
fp@325:     INIT_LIST_HEAD(&domain->data_regs);
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@178:     if (kobject_set_name(&domain->kobj, "domain%i", index)) {
fp@178:         EC_ERR("Failed to set kobj name.\n");
fp@178:         return -1;
fp@178:     }
fp@178: 
fp@178:     return 0;
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@195:    Domain destructor.
fp@448:    Clears and frees a domain object.
fp@448: */
fp@448: 
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@448: /**
fp@448:    Clear and free domain.
fp@448:    This method is called by the kobject,
fp@448:    once there are no more references to it.
fp@195: */
fp@195: 
fp@195: void ec_domain_clear(struct kobject *kobj /**< kobject of the domain */)
fp@73: {
fp@293:     ec_datagram_t *datagram, *next;
fp@178:     ec_domain_t *domain;
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@325:     ec_domain_clear_data_regs(domain);
fp@178: 
fp@178:     kfree(domain);
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@339:    Registeres a PDO entry.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@325: int ec_domain_reg_pdo_entry(ec_domain_t *domain, /**< EtherCAT domain */
fp@325:                             ec_slave_t *slave, /**< slave */
fp@343:                             const ec_sii_pdo_t *pdo, /**< PDO */
fp@325:                             const ec_sii_pdo_entry_t *entry,
fp@325:                             /**< PDO registration entry */
fp@325:                             void **data_ptr /**< pointer to the process data
fp@325:                                                pointer */
fp@325:                             )
fp@325: {
fp@325:     ec_data_reg_t *data_reg;
fp@325:     const ec_sii_sync_t *sync;
fp@325:     const ec_sii_pdo_t *other_pdo;
fp@325:     const ec_sii_pdo_entry_t *other_entry;
fp@325:     unsigned int bit_offset, byte_offset, sync_found;
fp@325: 
fp@325:     // Find sync manager for PDO
fp@325:     sync_found = 0;
fp@325:     list_for_each_entry(sync, &slave->sii_syncs, list) {
fp@325:         if (sync->index == pdo->sync_index) {
fp@325:             sync_found = 1;
fp@325:             break;
fp@325:         }
fp@325:     }
fp@325: 
fp@325:     if (!sync_found) {
fp@325:         EC_ERR("No sync manager for PDO 0x%04X:%i.",
fp@325:                pdo->index, entry->subindex);
fp@325:         return -1;
fp@325:     }
fp@325: 
fp@416:     // Calculate offset (in sync manager) for process data pointer
fp@325:     bit_offset = 0;
fp@325:     byte_offset = 0;
fp@325:     list_for_each_entry(other_pdo, &slave->sii_pdos, list) {
fp@325:         if (other_pdo->sync_index != sync->index) continue;
fp@325: 
fp@353:         list_for_each_entry(other_entry, &other_pdo->entries, list) {
fp@325:             if (other_entry == entry) {
fp@325:                 byte_offset = bit_offset / 8;
fp@325:                 break;
fp@325:             }
fp@325:             bit_offset += other_entry->bit_length;
fp@325:         }
fp@325:     }
fp@325: 
fp@325:     // Allocate memory for data registration object
fp@325:     if (!(data_reg =
fp@325:           (ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
fp@325:         EC_ERR("Failed to allocate data registration.\n");
fp@73:         return -1;
fp@73:     }
fp@73: 
fp@160:     if (ec_slave_prepare_fmmu(slave, domain, sync)) {
fp@84:         EC_ERR("FMMU configuration failed.\n");
fp@325:         kfree(data_reg);
fp@73:         return -1;
fp@73:     }
fp@73: 
fp@325:     data_reg->slave = slave;
fp@325:     data_reg->sync = sync;
fp@325:     data_reg->sync_offset = byte_offset;
fp@325:     data_reg->data_ptr = data_ptr;
fp@325: 
fp@325:     list_add_tail(&data_reg->list, &domain->data_regs);
fp@446: 
fp@446:     ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
fp@446: 
fp@73:     return 0;
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@416:    Registeres a PDO range.
fp@416:    \return 0 in case of success, else < 0
fp@416: */
fp@416: 
fp@416: int ec_domain_reg_pdo_range(ec_domain_t *domain, /**< EtherCAT domain */
fp@416:                             ec_slave_t *slave, /**< slave */
fp@416:                             ec_direction_t dir, /**< data direction */
fp@416:                             uint16_t offset, /**< offset */
fp@416:                             uint16_t length, /**< length */
fp@416:                             void **data_ptr /**< pointer to the process data
fp@416:                                                pointer */
fp@416:                             )
fp@416: {
fp@416:     ec_data_reg_t *data_reg;
fp@416:     ec_sii_sync_t *sync;
fp@416:     unsigned int sync_found, sync_index;
fp@416:     uint16_t sync_length;
fp@416: 
fp@416:     switch (dir) {
fp@416:         case EC_DIR_OUTPUT: sync_index = 2; break;
fp@416:         case EC_DIR_INPUT:  sync_index = 3; break;
fp@416:         default:
fp@416:             EC_ERR("Invalid direction!\n");
fp@416:             return -1;
fp@416:     }
fp@416: 
fp@416:     // Find sync manager
fp@416:     sync_found = 0;
fp@416:     list_for_each_entry(sync, &slave->sii_syncs, list) {
fp@416:         if (sync->index == sync_index) {
fp@416:             sync_found = 1;
fp@416:             break;
fp@416:         }
fp@416:     }
fp@416: 
fp@416:     if (!sync_found) {
fp@416:         EC_ERR("No sync manager found for PDO range.\n");
fp@416:         return -1;
fp@416:     }
fp@416: 
fp@416:      // Allocate memory for data registration object
fp@416:     if (!(data_reg =
fp@416:           (ec_data_reg_t *) kmalloc(sizeof(ec_data_reg_t), GFP_KERNEL))) {
fp@416:         EC_ERR("Failed to allocate data registration.\n");
fp@416:         return -1;
fp@416:     }
fp@416: 
fp@416:     if (ec_slave_prepare_fmmu(slave, domain, sync)) {
fp@416:         EC_ERR("FMMU configuration failed.\n");
fp@416:         kfree(data_reg);
fp@416:         return -1;
fp@416:     }
fp@416: 
fp@416:     data_reg->slave = slave;
fp@416:     data_reg->sync = sync;
fp@416:     data_reg->sync_offset = offset;
fp@416:     data_reg->data_ptr = data_ptr;
fp@416: 
fp@416:     // estimate sync manager length
fp@416:     sync_length = offset + length;
fp@416:     if (sync->est_length < sync_length) {
fp@416:         sync->est_length = sync_length;
fp@416:         if (domain->master->debug_level) {
fp@416:             EC_DBG("Estimating length of sync manager %i of slave %i to %i.\n",
fp@416:                    sync->index, slave->ring_position, sync_length);
fp@416:         }
fp@416:     }
fp@416: 
fp@416:     list_add_tail(&data_reg->list, &domain->data_regs);
fp@446: 
fp@446:     ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
fp@446: 
fp@416:     return 0;
fp@416: }
fp@416: 
fp@416: /*****************************************************************************/
fp@416: 
fp@416: /**
fp@339:    Clears the list of the data registrations.
fp@195: */
fp@195: 
fp@325: void ec_domain_clear_data_regs(ec_domain_t *domain /**< EtherCAT domain */)
fp@325: {
fp@325:     ec_data_reg_t *data_reg, *next;
fp@325: 
fp@325:     list_for_each_entry_safe(data_reg, next, &domain->data_regs, list) {
fp@325:         list_del(&data_reg->list);
fp@325:         kfree(data_reg);
fp@98:     }
fp@98: }
fp@98: 
fp@98: /*****************************************************************************/
fp@98: 
fp@98: /**
fp@293:    Allocates a process data datagram and appends it to the list.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@293: int ec_domain_add_datagram(ec_domain_t *domain, /**< EtherCAT domain */
fp@293:                            uint32_t offset, /**< logical offset */
fp@293:                            size_t data_size /**< size of the datagram data */
fp@293:                            )
fp@293: {
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@293: 
fp@293:     if (ec_datagram_lrw(datagram, offset, data_size)) {
fp@293:         kfree(datagram);
fp@144:         return -1;
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@144: /**
fp@195:    Creates a domain.
fp@195:    Reserves domain memory, calculates the logical addresses of the
fp@195:    corresponding FMMUs and sets the process data pointer of the registered
fp@325:    process data.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@195: int ec_domain_alloc(ec_domain_t *domain, /**< EtherCAT domain */
fp@275:                     uint32_t base_address /**< logical base address */
fp@73:                     )
fp@73: {
fp@325:     ec_data_reg_t *data_reg;
fp@73:     ec_slave_t *slave;
fp@73:     ec_fmmu_t *fmmu;
fp@325:     unsigned int i, j, datagram_count;
fp@325:     uint32_t pdo_off, pdo_off_datagram;
fp@325:     uint32_t datagram_offset;
fp@325:     size_t datagram_data_size, sync_size;
fp@293:     ec_datagram_t *datagram;
fp@73: 
fp@73:     domain->base_address = base_address;
fp@73: 
fp@275:     // calculate size of process data and allocate memory
fp@73:     domain->data_size = 0;
fp@325:     datagram_offset = base_address;
fp@325:     datagram_data_size = 0;
fp@325:     datagram_count = 0;
fp@182:     list_for_each_entry(slave, &domain->master->slaves, list) {
fp@73:         for (j = 0; j < slave->fmmu_count; j++) {
fp@73:             fmmu = &slave->fmmus[j];
fp@73:             if (fmmu->domain == domain) {
fp@73:                 fmmu->logical_start_address = base_address + domain->data_size;
fp@275:                 sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
fp@275:                 domain->data_size += sync_size;
fp@325:                 if (datagram_data_size + sync_size > EC_MAX_DATA_SIZE) {
fp@325:                     if (ec_domain_add_datagram(domain, datagram_offset,
fp@325:                                                datagram_data_size)) return -1;
fp@325:                     datagram_offset += datagram_data_size;
fp@325:                     datagram_data_size = 0;
fp@325:                     datagram_count++;
fp@144:                 }
fp@325:                 datagram_data_size += sync_size;
fp@73:             }
fp@73:         }
fp@73:     }
fp@73: 
fp@293:     // allocate last datagram
fp@325:     if (datagram_data_size) {
fp@325:         if (ec_domain_add_datagram(domain, datagram_offset,
fp@325:                                    datagram_data_size))
fp@144:             return -1;
fp@325:         datagram_count++;
fp@325:     }
fp@325: 
fp@325:     if (!datagram_count) {
fp@178:         EC_WARN("Domain %i contains no data!\n", domain->index);
fp@325:         ec_domain_clear_data_regs(domain);
fp@98:         return 0;
fp@98:     }
fp@98: 
fp@275:     // set all process data pointers
fp@325:     list_for_each_entry(data_reg, &domain->data_regs, list) {
fp@325:         for (i = 0; i < data_reg->slave->fmmu_count; i++) {
fp@325:             fmmu = &data_reg->slave->fmmus[i];
fp@325:             if (fmmu->domain == domain && fmmu->sync == data_reg->sync) {
fp@325:                 pdo_off = fmmu->logical_start_address + data_reg->sync_offset;
fp@293:                 // search datagram
fp@293:                 list_for_each_entry(datagram, &domain->datagrams, list) {
fp@325:                     pdo_off_datagram = pdo_off - datagram->address.logical;
fp@325:                     if (pdo_off >= datagram->address.logical &&
fp@325:                         pdo_off_datagram < datagram->mem_size) {
fp@325:                         *data_reg->data_ptr = datagram->data +
fp@325:                             pdo_off_datagram;
fp@144:                     }
fp@144:                 }
fp@325:                 if (!data_reg->data_ptr) {
fp@144:                     EC_ERR("Failed to assign data pointer!\n");
fp@144:                     return -1;
fp@144:                 }
fp@98:                 break;
fp@98:             }
fp@98:         }
fp@98:     }
fp@98: 
fp@377:     EC_INFO("Domain %i - Allocated %i bytes in %i datagram%s.\n",
fp@325:             domain->index, domain->data_size, datagram_count,
fp@325:             datagram_count == 1 ? "" : "s");
fp@325: 
fp@325:     ec_domain_clear_data_regs(domain);
fp@325: 
fp@325:     return 0;
fp@325: }
fp@325: 
fp@325: /*****************************************************************************/
fp@325: 
fp@325: /**
fp@325:    Places all process data datagrams in the masters datagram queue.
fp@325: */
fp@325: 
fp@446: void ec_domain_queue_datagrams(ec_domain_t *domain /**< EtherCAT domain */)
fp@325: {
fp@325:     ec_datagram_t *datagram;
fp@325: 
fp@325:     list_for_each_entry(datagram, &domain->datagrams, list) {
fp@325:         ec_master_queue_datagram(domain->master, datagram);
fp@195:     }
fp@195: }
fp@195: 
fp@195: /*****************************************************************************/
fp@195: 
fp@195: /**
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@178:         return sprintf(buffer, "%i\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@73: /**
fp@325:    Registers a PDO in a domain.
fp@325:    - If \a data_ptr is NULL, the slave is only validated.
fp@195:    \return pointer to the slave on success, else NULL
fp@199:    \ingroup RealtimeInterface
fp@73: */
fp@73: 
fp@325: ec_slave_t *ecrt_domain_register_pdo(ec_domain_t *domain,
fp@325:                                      /**< EtherCAT domain */
fp@325:                                      const char *address,
fp@325:                                      /**< ASCII address of the slave,
fp@325:                                         see ecrt_master_get_slave() */
fp@325:                                      uint32_t vendor_id,
fp@325:                                      /**< vendor ID */
fp@325:                                      uint32_t product_code,
fp@325:                                      /**< product code */
fp@325:                                      uint16_t pdo_index,
fp@325:                                      /**< PDO index */
fp@325:                                      uint8_t pdo_subindex,
fp@325:                                      /**< PDO subindex */
fp@325:                                      void **data_ptr
fp@325:                                      /**< address of the process data
fp@325:                                         pointer */
fp@325:                                      )
fp@73: {
fp@73:     ec_slave_t *slave;
fp@73:     ec_master_t *master;
fp@325:     const ec_sii_pdo_t *pdo;
fp@325:     const ec_sii_pdo_entry_t *entry;
fp@73: 
fp@73:     master = domain->master;
fp@73: 
fp@410:     // translate address and validate slave
fp@138:     if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
fp@410:     if (ec_slave_validate(slave, vendor_id, product_code)) return NULL;
fp@73: 
fp@446:     if (!data_ptr) return slave;
fp@112: 
fp@325:     list_for_each_entry(pdo, &slave->sii_pdos, list) {
fp@325:         list_for_each_entry(entry, &pdo->entries, list) {
fp@325:             if (entry->index != pdo_index
fp@325:                 || entry->subindex != pdo_subindex) continue;
fp@325: 
fp@446:             if (ec_domain_reg_pdo_entry(domain, slave, pdo, entry, data_ptr)) {
fp@446:                 return NULL;
fp@73:             }
fp@325: 
fp@325:             return slave;
fp@73:         }
fp@73:     }
fp@73: 
fp@325:     EC_ERR("Slave %i does not provide PDO 0x%04X:%i.\n",
fp@325:            slave->ring_position, pdo_index, pdo_subindex);
fp@73:     return NULL;
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@195:    Registeres a bunch of data fields.
fp@195:    Caution! The list has to be terminated with a NULL structure ({})!
fp@195:    \return 0 in case of success, else < 0
fp@199:    \ingroup RealtimeInterface
fp@73: */
fp@73: 
fp@325: int ecrt_domain_register_pdo_list(ec_domain_t *domain,
fp@325:                                   /**< EtherCAT domain */
fp@325:                                   const ec_pdo_reg_t *pdos
fp@325:                                   /**< array of PDO registrations */
fp@325:                                   )
fp@325: {
fp@325:     const ec_pdo_reg_t *pdo;
fp@325: 
fp@325:     for (pdo = pdos; pdo->slave_address; pdo++)
fp@325:         if (!ecrt_domain_register_pdo(domain, pdo->slave_address,
fp@325:                                       pdo->vendor_id,
fp@325:                                       pdo->product_code,
fp@325:                                       pdo->pdo_index,
fp@325:                                       pdo->pdo_subindex,
fp@325:                                       pdo->data_ptr))
fp@98:             return -1;
fp@98: 
fp@98:     return 0;
fp@98: }
fp@98: 
fp@98: /*****************************************************************************/
fp@98: 
fp@98: /**
fp@416:    Registers a PDO range in a domain.
fp@416:    - If \a data_ptr is NULL, the slave is only validated.
fp@416:    \return pointer to the slave on success, else NULL
fp@416:    \ingroup RealtimeInterface
fp@416: */
fp@416: 
fp@416: ec_slave_t *ecrt_domain_register_pdo_range(ec_domain_t *domain,
fp@416:                                            /**< EtherCAT domain */
fp@416:                                            const char *address,
fp@416:                                            /**< ASCII address of the slave,
fp@416:                                               see ecrt_master_get_slave() */
fp@416:                                            uint32_t vendor_id,
fp@416:                                            /**< vendor ID */
fp@416:                                            uint32_t product_code,
fp@416:                                            /**< product code */
fp@416:                                            ec_direction_t direction,
fp@416:                                            /**< data direction */
fp@416:                                            uint16_t offset,
fp@416:                                            /**< offset in slave's PDO range */
fp@416:                                            uint16_t length,
fp@416:                                            /**< length of this range */
fp@416:                                            void **data_ptr
fp@416:                                            /**< address of the process data
fp@416:                                               pointer */
fp@416:                                            )
fp@416: {
fp@416:     ec_slave_t *slave;
fp@416:     ec_master_t *master;
fp@416: 
fp@416:     master = domain->master;
fp@416: 
fp@416:     // translate address and validate slave
fp@416:     if (!(slave = ecrt_master_get_slave(master, address))) return NULL;
fp@416:     if (ec_slave_validate(slave, vendor_id, product_code)) return NULL;
fp@416: 
fp@446:     if (!data_ptr) return slave;
fp@416: 
fp@416:     if (ec_domain_reg_pdo_range(domain, slave,
fp@416:                                 direction, offset, length, data_ptr)) {
fp@416:         return NULL;
fp@416:     }
fp@416: 
fp@416:     return slave;
fp@416: }
fp@416: 
fp@416: /*****************************************************************************/
fp@416: 
fp@416: /**
fp@325:    Processes received process data and requeues the domain datagram(s).
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@195: void ecrt_domain_process(ec_domain_t *domain /**< EtherCAT 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@363:     domain->state = 0;
fp@293:     list_for_each_entry(datagram, &domain->datagrams, list) {
fp@325:         if (datagram->state == EC_DATAGRAM_RECEIVED) {
fp@293:             working_counter_sum += datagram->working_counter;
fp@98:         }
fp@363:         else {
fp@363:             domain->state = -1;
fp@363:         }
fp@98:     }
fp@73: 
fp@325:     if (working_counter_sum != domain->response_count) {
fp@332:         domain->working_counter_changes++;
fp@325:         domain->response_count = 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@332:             EC_INFO("Domain %i working counter change: %i\n", domain->index,
fp@332:                     domain->response_count);
fp@332:         }
fp@332:         else {
fp@332:             EC_INFO("Domain %i: %u WC changes. Current response count: %i\n",
fp@332:                     domain->index, domain->working_counter_changes,
fp@332:                     domain->response_count);
fp@332:         }
fp@332:         domain->working_counter_changes = 0;
fp@325:     }
fp@325: 
fp@446:     ec_domain_queue_datagrams(domain);
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@105: /**
fp@195:    Returns the state of a domain.
fp@293:    \return 0 if all datagrams were received, else -1.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@325: int ecrt_domain_state(const ec_domain_t *domain /**< EtherCAT domain */)
fp@105: {
fp@363:     return domain->state;
fp@105: }
fp@105: 
fp@105: /*****************************************************************************/
fp@105: 
fp@199: /** \cond */
fp@199: 
fp@325: EXPORT_SYMBOL(ecrt_domain_register_pdo);
fp@325: EXPORT_SYMBOL(ecrt_domain_register_pdo_list);
fp@416: EXPORT_SYMBOL(ecrt_domain_register_pdo_range);
fp@104: EXPORT_SYMBOL(ecrt_domain_process);
fp@105: EXPORT_SYMBOL(ecrt_domain_state);
fp@42: 
fp@199: /** \endcond */
fp@199: 
fp@199: /*****************************************************************************/