fp@39: /******************************************************************************
fp@0:  *
fp@39:  *  $Id$
fp@0:  *
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@39:  *****************************************************************************/
fp@0: 
fp@199: /**
fp@199:    \file
fp@199:    EtherCAT master methods.
fp@199: */
fp@199: 
fp@199: /*****************************************************************************/
fp@199: 
fp@24: #include <linux/module.h>
fp@0: #include <linux/kernel.h>
fp@0: #include <linux/string.h>
fp@0: #include <linux/slab.h>
fp@0: #include <linux/delay.h>
fp@0: 
fp@104: #include "../include/ecrt.h"
fp@54: #include "globals.h"
fp@54: #include "master.h"
fp@55: #include "slave.h"
fp@54: #include "device.h"
fp@293: #include "datagram.h"
fp@145: #include "ethernet.h"
fp@0: 
fp@39: /*****************************************************************************/
fp@0: 
fp@325: void ec_master_sync_io(ec_master_t *);
fp@318: void ec_master_idle_run(void *);
fp@251: void ec_master_eoe_run(unsigned long);
fp@179: ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *);
fp@268: ssize_t ec_store_master_attribute(struct kobject *, struct attribute *,
fp@268:                                   const char *, size_t);
fp@179: 
fp@179: /*****************************************************************************/
fp@179: 
fp@199: /** \cond */
fp@199: 
fp@334: EC_SYSFS_READ_ATTR(info);
fp@268: EC_SYSFS_READ_WRITE_ATTR(eeprom_write_enable);
fp@303: EC_SYSFS_READ_WRITE_ATTR(debug_level);
fp@178: 
fp@178: static struct attribute *ec_def_attrs[] = {
fp@334:     &attr_info,
fp@268:     &attr_eeprom_write_enable,
fp@303:     &attr_debug_level,
fp@178:     NULL,
fp@178: };
fp@178: 
fp@178: static struct sysfs_ops ec_sysfs_ops = {
fp@178:     .show = &ec_show_master_attribute,
fp@268:     .store = ec_store_master_attribute
fp@178: };
fp@178: 
fp@178: static struct kobj_type ktype_ec_master = {
fp@178:     .release = ec_master_clear,
fp@178:     .sysfs_ops = &ec_sysfs_ops,
fp@178:     .default_attrs = ec_def_attrs
fp@178: };
fp@178: 
fp@199: /** \endcond */
fp@199: 
fp@178: /*****************************************************************************/
fp@178: 
fp@0: /**
fp@195:    Master constructor.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@195: int ec_master_init(ec_master_t *master, /**< EtherCAT master */
fp@251:                    unsigned int index, /**< master index */
fp@361:                    unsigned int eoeif_count, /**< number of EoE interfaces */
fp@361:                    dev_t dev_num /**< number for XML cdev's */
fp@178:                    )
fp@178: {
fp@251:     ec_eoe_t *eoe, *next_eoe;
fp@251:     unsigned int i;
fp@251: 
fp@178:     EC_INFO("Initializing master %i.\n", index);
fp@178: 
fp@178:     master->index = index;
fp@98:     master->device = NULL;
fp@178:     master->reserved = 0;
fp@182:     INIT_LIST_HEAD(&master->slaves);
fp@293:     INIT_LIST_HEAD(&master->datagram_queue);
fp@95:     INIT_LIST_HEAD(&master->domains);
fp@251:     INIT_LIST_HEAD(&master->eoe_handlers);
fp@318:     INIT_WORK(&master->idle_work, ec_master_idle_run, (void *) master);
fp@226:     init_timer(&master->eoe_timer);
fp@251:     master->eoe_timer.function = ec_master_eoe_run;
fp@226:     master->eoe_timer.data = (unsigned long) master;
fp@251:     master->internal_lock = SPIN_LOCK_UNLOCKED;
fp@251:     master->eoe_running = 0;
fp@326:     master->eoe_checked = 0;
fp@335:     for (i = 0; i < HZ; i++) {
fp@335:         master->idle_cycle_times[i] = 0;
fp@335:         master->eoe_cycle_times[i] = 0;
fp@335:     }
fp@335:     master->idle_cycle_time_pos = 0;
fp@335:     master->eoe_cycle_time_pos = 0;
fp@251: 
fp@251:     // create workqueue
fp@251:     if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) {
fp@251:         EC_ERR("Failed to create master workqueue.\n");
fp@251:         goto out_return;
fp@251:     }
fp@251: 
fp@361:     // init XML character device
fp@361:     if (ec_xmldev_init(&master->xmldev, master, dev_num)) {
fp@361:         EC_ERR("Failed to init XML character device.\n");
fp@361:         goto out_clear_wq;
fp@361:     }
fp@361: 
fp@251:     // create EoE handlers
fp@321:     for (i = 0; i < eoeif_count; i++) {
fp@251:         if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) {
fp@251:             EC_ERR("Failed to allocate EoE-Object.\n");
fp@251:             goto out_clear_eoe;
fp@251:         }
fp@251:         if (ec_eoe_init(eoe)) {
fp@251:             kfree(eoe);
fp@251:             goto out_clear_eoe;
fp@251:         }
fp@251:         list_add_tail(&eoe->list, &master->eoe_handlers);
fp@251:     }
fp@251: 
fp@251:     // create state machine object
fp@251:     if (ec_fsm_init(&master->fsm, master)) goto out_clear_eoe;
fp@226: 
fp@195:     // init kobject and add it to the hierarchy
fp@178:     memset(&master->kobj, 0x00, sizeof(struct kobject));
fp@178:     kobject_init(&master->kobj);
fp@178:     master->kobj.ktype = &ktype_ec_master;
fp@178:     if (kobject_set_name(&master->kobj, "ethercat%i", index)) {
fp@178:         EC_ERR("Failed to set kobj name.\n");
fp@238:         kobject_put(&master->kobj);
fp@238:         return -1;
fp@226:     }
fp@144: 
fp@98:     ec_master_reset(master);
fp@178:     return 0;
fp@226: 
fp@251:  out_clear_eoe:
fp@251:     list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) {
fp@251:         list_del(&eoe->list);
fp@251:         ec_eoe_clear(eoe);
fp@251:         kfree(eoe);
fp@251:     }
fp@361:     ec_xmldev_clear(&master->xmldev);
fp@361:  out_clear_wq:
fp@238:     destroy_workqueue(master->workqueue);
fp@226:  out_return:
fp@226:     return -1;
fp@0: }
fp@0: 
fp@39: /*****************************************************************************/
fp@0: 
fp@0: /**
fp@195:    Master destructor.
fp@293:    Removes all pending datagrams, clears the slave list, clears all domains
fp@195:    and frees the device.
fp@195: */
fp@195: 
fp@195: void ec_master_clear(struct kobject *kobj /**< kobject of the master */)
fp@178: {
fp@178:     ec_master_t *master = container_of(kobj, ec_master_t, kobj);
fp@251:     ec_eoe_t *eoe, *next_eoe;
fp@178: 
fp@178:     EC_INFO("Clearing master %i...\n", master->index);
fp@178: 
fp@73:     ec_master_reset(master);
fp@238:     ec_fsm_clear(&master->fsm);
fp@238:     destroy_workqueue(master->workqueue);
fp@361:     ec_xmldev_clear(&master->xmldev);
fp@98: 
fp@251:     // clear EoE objects
fp@251:     list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) {
fp@251:         list_del(&eoe->list);
fp@251:         ec_eoe_clear(eoe);
fp@251:         kfree(eoe);
fp@251:     }
fp@251: 
fp@98:     if (master->device) {
fp@98:         ec_device_clear(master->device);
fp@98:         kfree(master->device);
fp@98:     }
fp@144: 
fp@178:     EC_INFO("Master %i cleared.\n", master->index);
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@195:    Resets the master.
fp@195:    Note: This function has to be called, everytime ec_master_release() is
fp@195:    called, to free the slave list, domains etc.
fp@195: */
fp@195: 
fp@195: void ec_master_reset(ec_master_t *master /**< EtherCAT master */)
fp@56: {
fp@293:     ec_datagram_t *datagram, *next_c;
fp@144:     ec_domain_t *domain, *next_d;
fp@251: 
fp@251:     ec_master_eoe_stop(master);
fp@306:     ec_master_idle_stop(master);
fp@238:     ec_master_clear_slaves(master);
fp@98: 
fp@293:     // empty datagram queue
fp@293:     list_for_each_entry_safe(datagram, next_c,
fp@293:                              &master->datagram_queue, queue) {
fp@345:         datagram->state = EC_DATAGRAM_ERROR;
fp@293:         list_del_init(&datagram->queue);
fp@98:     }
fp@98: 
fp@195:     // clear domains
fp@144:     list_for_each_entry_safe(domain, next_d, &master->domains, list) {
fp@98:         list_del(&domain->list);
fp@178:         kobject_del(&domain->kobj);
fp@178:         kobject_put(&domain->kobj);
fp@98:     }
fp@98: 
fp@293:     master->datagram_index = 0;
fp@98:     master->debug_level = 0;
fp@130: 
fp@98:     master->stats.timeouts = 0;
fp@98:     master->stats.corrupted = 0;
fp@332:     master->stats.skipped = 0;
fp@98:     master->stats.unmatched = 0;
fp@344:     master->stats.output_jiffies = 0;
fp@191: 
fp@305:     master->mode = EC_MASTER_MODE_ORPHANED;
fp@204: 
fp@204:     master->request_cb = NULL;
fp@204:     master->release_cb = NULL;
fp@204:     master->cb_data = NULL;
fp@238: 
fp@268:     master->eeprom_write_enable = 0;
fp@268: 
fp@238:     ec_fsm_reset(&master->fsm);
fp@238: }
fp@238: 
fp@238: /*****************************************************************************/
fp@238: 
fp@238: /**
fp@238:    Clears all slaves.
fp@238: */
fp@238: 
fp@238: void ec_master_clear_slaves(ec_master_t *master)
fp@238: {
fp@238:     ec_slave_t *slave, *next_slave;
fp@238: 
fp@238:     list_for_each_entry_safe(slave, next_slave, &master->slaves, list) {
fp@238:         list_del(&slave->list);
fp@238:         kobject_del(&slave->kobj);
fp@238:         kobject_put(&slave->kobj);
fp@238:     }
fp@238:     master->slave_count = 0;
fp@74: }
fp@74: 
fp@74: /*****************************************************************************/
fp@74: 
fp@74: /**
fp@293:    Places a datagram in the datagram queue.
fp@293: */
fp@293: 
fp@293: void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
fp@293:                               ec_datagram_t *datagram /**< datagram */
fp@293:                               )
fp@293: {
fp@293:     ec_datagram_t *queued_datagram;
fp@293: 
fp@293:     // check, if the datagram is already queued
fp@293:     list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
fp@293:         if (queued_datagram == datagram) {
fp@332:             master->stats.skipped++;
fp@332:             ec_master_output_stats(master);
fp@325:             datagram->state = EC_DATAGRAM_QUEUED;
fp@98:             return;
fp@98:         }
fp@98:     }
fp@98: 
fp@293:     list_add_tail(&datagram->queue, &master->datagram_queue);
fp@325:     datagram->state = EC_DATAGRAM_QUEUED;
fp@293: }
fp@293: 
fp@293: /*****************************************************************************/
fp@293: 
fp@293: /**
fp@293:    Sends the datagrams in the queue.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@293: void ec_master_send_datagrams(ec_master_t *master /**< EtherCAT master */)
fp@293: {
fp@293:     ec_datagram_t *datagram;
fp@293:     size_t datagram_size;
fp@98:     uint8_t *frame_data, *cur_data;
fp@98:     void *follows_word;
fp@344:     cycles_t cycles_start, cycles_end;
fp@293:     unsigned int frame_count, more_datagrams_waiting;
fp@176: 
fp@176:     frame_count = 0;
fp@344:     cycles_start = get_cycles();
fp@208: 
fp@303:     if (unlikely(master->debug_level > 1))
fp@293:         EC_DBG("ec_master_send_datagrams\n");
fp@98: 
fp@176:     do {
fp@195:         // fetch pointer to transmit socket buffer
fp@176:         frame_data = ec_device_tx_data(master->device);
fp@176:         cur_data = frame_data + EC_FRAME_HEADER_SIZE;
fp@176:         follows_word = NULL;
fp@293:         more_datagrams_waiting = 0;
fp@293: 
fp@293:         // fill current frame with datagrams
fp@293:         list_for_each_entry(datagram, &master->datagram_queue, queue) {
fp@325:             if (datagram->state != EC_DATAGRAM_QUEUED) continue;
fp@293: 
fp@293:             // does the current datagram fit in the frame?
fp@293:             datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size
fp@293:                 + EC_DATAGRAM_FOOTER_SIZE;
fp@293:             if (cur_data - frame_data + datagram_size > ETH_DATA_LEN) {
fp@293:                 more_datagrams_waiting = 1;
fp@176:                 break;
fp@176:             }
fp@176: 
fp@325:             datagram->state = EC_DATAGRAM_SENT;
fp@344:             datagram->cycles_sent = cycles_start;
fp@293:             datagram->index = master->datagram_index++;
fp@176: 
fp@303:             if (unlikely(master->debug_level > 1))
fp@293:                 EC_DBG("adding datagram 0x%02X\n", datagram->index);
fp@293: 
fp@293:             // set "datagram following" flag in previous frame
fp@176:             if (follows_word)
fp@176:                 EC_WRITE_U16(follows_word, EC_READ_U16(follows_word) | 0x8000);
fp@176: 
fp@293:             // EtherCAT datagram header
fp@293:             EC_WRITE_U8 (cur_data,     datagram->type);
fp@293:             EC_WRITE_U8 (cur_data + 1, datagram->index);
fp@293:             EC_WRITE_U32(cur_data + 2, datagram->address.logical);
fp@293:             EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF);
fp@176:             EC_WRITE_U16(cur_data + 8, 0x0000);
fp@176:             follows_word = cur_data + 6;
fp@293:             cur_data += EC_DATAGRAM_HEADER_SIZE;
fp@293: 
fp@293:             // EtherCAT datagram data
fp@293:             memcpy(cur_data, datagram->data, datagram->data_size);
fp@293:             cur_data += datagram->data_size;
fp@293: 
fp@293:             // EtherCAT datagram footer
fp@195:             EC_WRITE_U16(cur_data, 0x0000); // reset working counter
fp@293:             cur_data += EC_DATAGRAM_FOOTER_SIZE;
fp@176:         }
fp@176: 
fp@176:         if (cur_data - frame_data == EC_FRAME_HEADER_SIZE) {
fp@303:             if (unlikely(master->debug_level > 1))
fp@176:                 EC_DBG("nothing to send.\n");
fp@176:             break;
fp@176:         }
fp@176: 
fp@176:         // EtherCAT frame header
fp@176:         EC_WRITE_U16(frame_data, ((cur_data - frame_data
fp@176:                                    - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
fp@176: 
fp@195:         // pad frame
fp@211:         while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN)
fp@176:             EC_WRITE_U8(cur_data++, 0x00);
fp@98: 
fp@303:         if (unlikely(master->debug_level > 1))
fp@176:             EC_DBG("frame size: %i\n", cur_data - frame_data);
fp@176: 
fp@195:         // send frame
fp@176:         ec_device_send(master->device, cur_data - frame_data);
fp@176:         frame_count++;
fp@176:     }
fp@293:     while (more_datagrams_waiting);
fp@98: 
fp@303:     if (unlikely(master->debug_level > 1)) {
fp@344:         cycles_end = get_cycles();
fp@293:         EC_DBG("ec_master_send_datagrams sent %i frames in %ius.\n",
fp@344:                frame_count,
fp@344:                (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
fp@176:     }
fp@176: }
fp@176: 
fp@176: /*****************************************************************************/
fp@176: 
fp@176: /**
fp@176:    Processes a received frame.
fp@176:    This function is called by the network driver for every received frame.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@331: void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */
fp@331:                                  const uint8_t *frame_data, /**< frame data */
fp@331:                                  size_t size /**< size of the received data */
fp@331:                                  )
fp@98: {
fp@98:     size_t frame_size, data_size;
fp@293:     uint8_t datagram_type, datagram_index;
fp@98:     unsigned int cmd_follows, matched;
fp@98:     const uint8_t *cur_data;
fp@293:     ec_datagram_t *datagram;
fp@98: 
fp@98:     if (unlikely(size < EC_FRAME_HEADER_SIZE)) {
fp@98:         master->stats.corrupted++;
fp@98:         ec_master_output_stats(master);
fp@98:         return;
fp@98:     }
fp@98: 
fp@98:     cur_data = frame_data;
fp@98: 
fp@195:     // check length of entire frame
fp@98:     frame_size = EC_READ_U16(cur_data) & 0x07FF;
fp@98:     cur_data += EC_FRAME_HEADER_SIZE;
fp@98: 
fp@98:     if (unlikely(frame_size > size)) {
fp@98:         master->stats.corrupted++;
fp@98:         ec_master_output_stats(master);
fp@98:         return;
fp@98:     }
fp@98: 
fp@98:     cmd_follows = 1;
fp@98:     while (cmd_follows) {
fp@293:         // process datagram header
fp@293:         datagram_type  = EC_READ_U8 (cur_data);
fp@293:         datagram_index = EC_READ_U8 (cur_data + 1);
fp@293:         data_size      = EC_READ_U16(cur_data + 6) & 0x07FF;
fp@293:         cmd_follows    = EC_READ_U16(cur_data + 6) & 0x8000;
fp@293:         cur_data += EC_DATAGRAM_HEADER_SIZE;
fp@98: 
fp@98:         if (unlikely(cur_data - frame_data
fp@293:                      + data_size + EC_DATAGRAM_FOOTER_SIZE > size)) {
fp@98:             master->stats.corrupted++;
fp@98:             ec_master_output_stats(master);
fp@98:             return;
fp@98:         }
fp@98: 
fp@293:         // search for matching datagram in the queue
fp@98:         matched = 0;
fp@293:         list_for_each_entry(datagram, &master->datagram_queue, queue) {
fp@325:             if (datagram->state == EC_DATAGRAM_SENT
fp@293:                 && datagram->type == datagram_type
fp@293:                 && datagram->index == datagram_index
fp@293:                 && datagram->data_size == data_size) {
fp@98:                 matched = 1;
fp@98:                 break;
fp@98:             }
fp@98:         }
fp@98: 
fp@293:         // no matching datagram was found
fp@98:         if (!matched) {
fp@98:             master->stats.unmatched++;
fp@98:             ec_master_output_stats(master);
fp@293:             cur_data += data_size + EC_DATAGRAM_FOOTER_SIZE;
fp@98:             continue;
fp@98:         }
fp@98: 
fp@345:         // copy received data into the datagram memory
fp@293:         memcpy(datagram->data, cur_data, data_size);
fp@98:         cur_data += data_size;
fp@98: 
fp@293:         // set the datagram's working counter
fp@293:         datagram->working_counter = EC_READ_U16(cur_data);
fp@293:         cur_data += EC_DATAGRAM_FOOTER_SIZE;
fp@293: 
fp@293:         // dequeue the received datagram
fp@325:         datagram->state = EC_DATAGRAM_RECEIVED;
fp@293:         list_del_init(&datagram->queue);
fp@293:     }
fp@293: }
fp@293: 
fp@293: /*****************************************************************************/
fp@293: 
fp@293: /**
fp@195:    Scans the EtherCAT bus for slaves.
fp@195:    Creates a list of slave structures for further processing.
fp@195:    \return 0 in case of success, else < 0
fp@195: */
fp@195: 
fp@195: int ec_master_bus_scan(ec_master_t *master /**< EtherCAT master */)
fp@98: {
fp@325:     ec_fsm_t *fsm = &master->fsm;
fp@325: 
fp@325:     ec_fsm_startup(fsm); // init startup state machine
fp@325: 
fp@325:     do {
fp@325:         ec_fsm_execute(fsm);
fp@325:         ec_master_sync_io(master);
fp@325:     }
fp@325:     while (ec_fsm_startup_running(fsm));
fp@325: 
fp@325:     if (!ec_fsm_startup_success(fsm)) {
fp@325:         ec_master_clear_slaves(master);
fp@90:         return -1;
fp@90:     }
fp@73: 
fp@73:     return 0;
fp@54: }
fp@54: 
fp@54: /*****************************************************************************/
fp@54: 
fp@54: /**
fp@195:    Output statistics in cyclic mode.
fp@195:    This function outputs statistical data on demand, but not more often than
fp@195:    necessary. The output happens at most once a second.
fp@195: */
fp@195: 
fp@195: void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */)
fp@98: {
fp@344:     if (unlikely(jiffies - master->stats.output_jiffies >= HZ)) {
fp@344:         master->stats.output_jiffies = jiffies;
fp@344: 
fp@98:         if (master->stats.timeouts) {
fp@293:             EC_WARN("%i datagrams TIMED OUT!\n", master->stats.timeouts);
fp@98:             master->stats.timeouts = 0;
fp@98:         }
fp@98:         if (master->stats.corrupted) {
fp@98:             EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted);
fp@98:             master->stats.corrupted = 0;
fp@98:         }
fp@332:         if (master->stats.skipped) {
fp@332:             EC_WARN("%i datagram(s) SKIPPED!\n", master->stats.skipped);
fp@332:             master->stats.skipped = 0;
fp@332:         }
fp@98:         if (master->stats.unmatched) {
fp@293:             EC_WARN("%i datagram(s) UNMATCHED!\n", master->stats.unmatched);
fp@98:             master->stats.unmatched = 0;
fp@98:         }
fp@73:     }
fp@54: }
fp@54: 
fp@68: /*****************************************************************************/
fp@68: 
fp@68: /**
fp@306:    Starts the Idle mode.
fp@306: */
fp@306: 
fp@306: void ec_master_idle_start(ec_master_t *master /**< EtherCAT master */)
fp@306: {
fp@306:     if (master->mode == EC_MASTER_MODE_IDLE) return;
fp@191: 
fp@325:     if (master->mode == EC_MASTER_MODE_OPERATION) {
fp@306:         EC_ERR("ec_master_idle_start: Master already running!\n");
fp@191:         return;
fp@191:     }
fp@191: 
fp@306:     EC_INFO("Starting Idle mode.\n");
fp@306: 
fp@306:     master->mode = EC_MASTER_MODE_IDLE;
fp@238:     ec_fsm_reset(&master->fsm);
fp@306:     queue_delayed_work(master->workqueue, &master->idle_work, 1);
fp@306: }
fp@306: 
fp@306: /*****************************************************************************/
fp@306: 
fp@306: /**
fp@306:    Stops the Idle mode.
fp@306: */
fp@306: 
fp@306: void ec_master_idle_stop(ec_master_t *master /**< EtherCAT master */)
fp@306: {
fp@306:     if (master->mode != EC_MASTER_MODE_IDLE) return;
fp@191: 
fp@251:     ec_master_eoe_stop(master);
fp@251: 
fp@306:     EC_INFO("Stopping Idle mode.\n");
fp@306:     master->mode = EC_MASTER_MODE_ORPHANED; // this is important for the
fp@306:                                             // IDLE work function to not
fp@306:                                             // queue itself again
fp@306: 
fp@306:     if (!cancel_delayed_work(&master->idle_work)) {
fp@236:         flush_workqueue(master->workqueue);
fp@236:     }
fp@236: 
fp@238:     ec_master_clear_slaves(master);
fp@191: }
fp@191: 
fp@191: /*****************************************************************************/
fp@191: 
fp@191: /**
fp@306:    Idle mode function.
fp@306: */
fp@306: 
fp@318: void ec_master_idle_run(void *data /**< master pointer */)
fp@191: {
fp@191:     ec_master_t *master = (ec_master_t *) data;
fp@344:     cycles_t cycles_start, cycles_end;
fp@251: 
fp@251:     // aquire master lock
fp@251:     spin_lock_bh(&master->internal_lock);
fp@191: 
fp@344:     cycles_start = get_cycles();
fp@325:     ecrt_master_receive(master);
fp@195: 
fp@251:     // execute master state machine
fp@238:     ec_fsm_execute(&master->fsm);
fp@195: 
fp@325:     ecrt_master_send(master);
fp@344:     cycles_end = get_cycles();
fp@191: 
fp@251:     // release master lock
fp@251:     spin_unlock_bh(&master->internal_lock);
fp@251: 
fp@335:     master->idle_cycle_times[master->idle_cycle_time_pos]
fp@344:         = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz;
fp@335:     master->idle_cycle_time_pos++;
fp@335:     master->idle_cycle_time_pos %= HZ;
fp@334: 
fp@306:     if (master->mode == EC_MASTER_MODE_IDLE)
fp@306:         queue_delayed_work(master->workqueue, &master->idle_work, 1);
fp@191: }
fp@191: 
fp@191: /*****************************************************************************/
fp@191: 
fp@191: /**
fp@325:    Initializes a sync manager configuration page with EEPROM data.
fp@195:    The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes.
fp@195: */
fp@195: 
fp@325: void ec_sync_config(const ec_sii_sync_t *sync, /**< sync manager */
fp@275:                     const ec_slave_t *slave, /**< EtherCAT slave */
fp@195:                     uint8_t *data /**> configuration memory */
fp@73:                     )
fp@73: {
fp@275:     size_t sync_size;
fp@275: 
fp@275:     sync_size = ec_slave_calc_sync_size(slave, sync);
fp@275: 
fp@303:     if (slave->master->debug_level) {
fp@303:         EC_INFO("Slave %i, sync manager %i:\n", slave->ring_position,
fp@303:                 sync->index);
fp@303:         EC_INFO("  Address: 0x%04X\n", sync->physical_start_address);
fp@303:         EC_INFO("     Size: %i\n", sync_size);
fp@303:         EC_INFO("  Control: 0x%02X\n", sync->control_register);
fp@303:     }
fp@303: 
fp@136:     EC_WRITE_U16(data,     sync->physical_start_address);
fp@298:     EC_WRITE_U16(data + 2, sync_size);
fp@136:     EC_WRITE_U8 (data + 4, sync->control_register);
fp@136:     EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
fp@136:     EC_WRITE_U16(data + 6, sync->enable ? 0x0001 : 0x0000); // enable
fp@136: }
fp@136: 
fp@136: /*****************************************************************************/
fp@136: 
fp@136: /**
fp@195:    Initializes an FMMU configuration page.
fp@195:    The referenced memory (\a data) must be at least EC_FMMU_SIZE bytes.
fp@195: */
fp@195: 
fp@195: void ec_fmmu_config(const ec_fmmu_t *fmmu, /**< FMMU */
fp@275:                     const ec_slave_t *slave, /**< EtherCAT slave */
fp@195:                     uint8_t *data /**> configuration memory */
fp@73:                     )
fp@73: {
fp@275:     size_t sync_size;
fp@275: 
fp@275:     sync_size = ec_slave_calc_sync_size(slave, fmmu->sync);
fp@275: 
fp@77:     EC_WRITE_U32(data,      fmmu->logical_start_address);
fp@275:     EC_WRITE_U16(data + 4,  sync_size); // size of fmmu
fp@195:     EC_WRITE_U8 (data + 6,  0x00); // logical start bit
fp@195:     EC_WRITE_U8 (data + 7,  0x07); // logical end bit
fp@77:     EC_WRITE_U16(data + 8,  fmmu->sync->physical_start_address);
fp@195:     EC_WRITE_U8 (data + 10, 0x00); // physical start bit
fp@325:     EC_WRITE_U8 (data + 11, ((fmmu->sync->control_register & 0x04)
fp@325:                              ? 0x02 : 0x01));
fp@195:     EC_WRITE_U16(data + 12, 0x0001); // enable
fp@195:     EC_WRITE_U16(data + 14, 0x0000); // reserved
fp@195: }
fp@195: 
fp@195: /*****************************************************************************/
fp@195: 
fp@195: /**
fp@334:    Formats master information for SysFS read access.
fp@334:    \return number of bytes written
fp@334: */
fp@334: 
fp@334: ssize_t ec_master_info(ec_master_t *master, /**< EtherCAT master */
fp@334:                        char *buffer /**< memory to store data */
fp@334:                        )
fp@334: {
fp@334:     off_t off = 0;
fp@336:     ec_eoe_t *eoe;
fp@335:     uint32_t cur, sum, min, max, pos, i;
fp@334: 
fp@356:     off += sprintf(buffer + off, "\nVersion: " EC_COMPILE_INFO);
fp@334:     off += sprintf(buffer + off, "\nMode: ");
fp@334:     switch (master->mode) {
fp@334:         case EC_MASTER_MODE_ORPHANED:
fp@334:             off += sprintf(buffer + off, "ORPHANED");
fp@335:             break;
fp@334:         case EC_MASTER_MODE_IDLE:
fp@334:             off += sprintf(buffer + off, "IDLE");
fp@335:             break;
fp@334:         case EC_MASTER_MODE_OPERATION:
fp@334:             off += sprintf(buffer + off, "OPERATION");
fp@335:             break;
fp@334:     }
fp@334: 
fp@339:     off += sprintf(buffer + off, "\nSlaves: %i\n",
fp@334:                    master->slave_count);
fp@334: 
fp@335:     off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n");
fp@335: 
fp@335:     sum = 0;
fp@335:     min = 0xFFFFFFFF;
fp@335:     max = 0;
fp@335:     pos = master->idle_cycle_time_pos;
fp@335:     for (i = 0; i < HZ; i++) {
fp@335:         cur = master->idle_cycle_times[(i + pos) % HZ];
fp@335:         sum += cur;
fp@335:         if (cur < min) min = cur;
fp@335:         if (cur > max) max = cur;
fp@335:     }
fp@335:     off += sprintf(buffer + off, "  Idle cycle: %u / %u.%u / %u\n",
fp@335:                    min, sum / HZ, (sum * 100 / HZ) % 100, max);
fp@335: 
fp@335:     sum = 0;
fp@335:     min = 0xFFFFFFFF;
fp@335:     max = 0;
fp@335:     pos = master->eoe_cycle_time_pos;
fp@335:     for (i = 0; i < HZ; i++) {
fp@335:         cur = master->eoe_cycle_times[(i + pos) % HZ];
fp@335:         sum += cur;
fp@335:         if (cur < min) min = cur;
fp@335:         if (cur > max) max = cur;
fp@335:     }
fp@335:     off += sprintf(buffer + off, "  EoE cycle: %u / %u.%u / %u\n",
fp@335:                    min, sum / HZ, (sum * 100 / HZ) % 100, max);
fp@334: 
fp@336:     if (!list_empty(&master->eoe_handlers))
fp@339:         off += sprintf(buffer + off, "\nEoE statistics (RX/TX) [bps]:\n");
fp@336:     list_for_each_entry(eoe, &master->eoe_handlers, list) {
fp@341:         off += sprintf(buffer + off, "  %s: %u / %u (%u KB/s)\n",
fp@341:                        eoe->dev->name, eoe->rx_rate, eoe->tx_rate,
fp@341:                        ((eoe->rx_rate + eoe->tx_rate) / 8 + 512) / 1024);
fp@336:     }
fp@336: 
fp@334:     off += sprintf(buffer + off, "\n");
fp@334: 
fp@334:     return off;
fp@334: }
fp@334: 
fp@334: /*****************************************************************************/
fp@334: 
fp@334: /**
fp@195:    Formats attribute data for SysFS read access.
fp@195:    \return number of bytes to read
fp@195: */
fp@195: 
fp@195: ssize_t ec_show_master_attribute(struct kobject *kobj, /**< kobject */
fp@195:                                  struct attribute *attr, /**< attribute */
fp@195:                                  char *buffer /**< memory to store data */
fp@178:                                  )
fp@178: {
fp@178:     ec_master_t *master = container_of(kobj, ec_master_t, kobj);
fp@178: 
fp@334:     if (attr == &attr_info) {
fp@334:         return ec_master_info(master, buffer);
fp@191:     }
fp@303:     else if (attr == &attr_debug_level) {
fp@303:         return sprintf(buffer, "%i\n", master->debug_level);
fp@303:     }
fp@313:     else if (attr == &attr_eeprom_write_enable) {
fp@313:         return sprintf(buffer, "%i\n", master->eeprom_write_enable);
fp@313:     }
fp@178: 
fp@178:     return 0;
fp@178: }
fp@178: 
fp@178: /*****************************************************************************/
fp@178: 
fp@178: /**
fp@268:    Formats attribute data for SysFS write access.
fp@268:    \return number of bytes processed, or negative error code
fp@268: */
fp@268: 
fp@268: ssize_t ec_store_master_attribute(struct kobject *kobj, /**< slave's kobject */
fp@268:                                   struct attribute *attr, /**< attribute */
fp@268:                                   const char *buffer, /**< memory with data */
fp@268:                                   size_t size /**< size of data to store */
fp@268:                                   )
fp@268: {
fp@268:     ec_master_t *master = container_of(kobj, ec_master_t, kobj);
fp@268: 
fp@268:     if (attr == &attr_eeprom_write_enable) {
fp@268:         if (!strcmp(buffer, "1\n")) {
fp@268:             master->eeprom_write_enable = 1;
fp@268:             EC_INFO("Slave EEPROM writing enabled.\n");
fp@268:             return size;
fp@268:         }
fp@268:         else if (!strcmp(buffer, "0\n")) {
fp@268:             master->eeprom_write_enable = 0;
fp@268:             EC_INFO("Slave EEPROM writing disabled.\n");
fp@268:             return size;
fp@268:         }
fp@268: 
fp@268:         EC_ERR("Invalid value for eeprom_write_enable!\n");
fp@268: 
fp@268:         if (master->eeprom_write_enable) {
fp@268:             master->eeprom_write_enable = 0;
fp@268:             EC_INFO("Slave EEPROM writing disabled.\n");
fp@268:         }
fp@268:     }
fp@303:     else if (attr == &attr_debug_level) {
fp@303:         if (!strcmp(buffer, "0\n")) {
fp@303:             master->debug_level = 0;
fp@303:         }
fp@303:         else if (!strcmp(buffer, "1\n")) {
fp@303:             master->debug_level = 1;
fp@303:         }
fp@303:         else if (!strcmp(buffer, "2\n")) {
fp@303:             master->debug_level = 2;
fp@303:         }
fp@303:         else {
fp@303:             EC_ERR("Invalid debug level value!\n");
fp@303:             return -EINVAL;
fp@303:         }
fp@303: 
fp@303:         EC_INFO("Master debug level set to %i.\n", master->debug_level);
fp@303:         return size;
fp@303:     }
fp@268: 
fp@268:     return -EINVAL;
fp@268: }
fp@268: 
fp@268: /*****************************************************************************/
fp@268: 
fp@268: /**
fp@326:    Starts Ethernet-over-EtherCAT processing on demand.
fp@251: */
fp@251: 
fp@251: void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
fp@251: {
fp@251:     ec_eoe_t *eoe;
fp@251:     ec_slave_t *slave;
fp@251:     unsigned int coupled, found;
fp@251: 
fp@326:     if (master->eoe_running || master->eoe_checked) return;
fp@326: 
fp@326:     master->eoe_checked = 1;
fp@251: 
fp@325:     // if the locking callbacks are not set in operation mode,
fp@325:     // the EoE timer my not be started.
fp@325:     if (master->mode == EC_MASTER_MODE_OPERATION
fp@326:         && (!master->request_cb || !master->release_cb)) {
fp@326:         EC_INFO("No EoE processing because of missing locking callbacks.\n");
fp@326:         return;
fp@326:     }
fp@325: 
fp@251:     // decouple all EoE handlers
fp@251:     list_for_each_entry(eoe, &master->eoe_handlers, list)
fp@251:         eoe->slave = NULL;
fp@325: 
fp@325:     // couple a free EoE handler to every EoE-capable slave
fp@251:     coupled = 0;
fp@251:     list_for_each_entry(slave, &master->slaves, list) {
fp@251:         if (!(slave->sii_mailbox_protocols & EC_MBOX_EOE)) continue;
fp@251: 
fp@251:         found = 0;
fp@251:         list_for_each_entry(eoe, &master->eoe_handlers, list) {
fp@251:             if (eoe->slave) continue;
fp@251:             eoe->slave = slave;
fp@251:             found = 1;
fp@251:             coupled++;
fp@251:             EC_INFO("Coupling device %s to slave %i.\n",
fp@251:                     eoe->dev->name, slave->ring_position);
fp@325:             if (eoe->opened) slave->requested_state = EC_SLAVE_STATE_OP;
fp@325:             else slave->requested_state = EC_SLAVE_STATE_INIT;
fp@291:             slave->error_flag = 0;
fp@253:             break;
fp@251:         }
fp@251: 
fp@251:         if (!found) {
fp@251:             EC_WARN("No EoE handler for slave %i!\n", slave->ring_position);
fp@251:             slave->requested_state = EC_SLAVE_STATE_INIT;
fp@291:             slave->error_flag = 0;
fp@251:         }
fp@251:     }
fp@251: 
fp@251:     if (!coupled) {
fp@251:         EC_INFO("No EoE handlers coupled.\n");
fp@251:         return;
fp@251:     }
fp@251: 
fp@251:     EC_INFO("Starting EoE processing.\n");
fp@251:     master->eoe_running = 1;
fp@251: 
fp@251:     // start EoE processing
fp@251:     master->eoe_timer.expires = jiffies + 10;
fp@251:     add_timer(&master->eoe_timer);
fp@251:     return;
fp@251: }
fp@251: 
fp@251: /*****************************************************************************/
fp@251: 
fp@251: /**
fp@251:    Stops the Ethernet-over-EtherCAT processing.
fp@251: */
fp@251: 
fp@251: void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */)
fp@251: {
fp@251:     ec_eoe_t *eoe;
fp@251: 
fp@326:     master->eoe_checked = 0;
fp@326: 
fp@251:     if (!master->eoe_running) return;
fp@251: 
fp@251:     EC_INFO("Stopping EoE processing.\n");
fp@251: 
fp@251:     del_timer_sync(&master->eoe_timer);
fp@251: 
fp@251:     // decouple all EoE handlers
fp@251:     list_for_each_entry(eoe, &master->eoe_handlers, list) {
fp@251:         if (eoe->slave) {
fp@251:             eoe->slave->requested_state = EC_SLAVE_STATE_INIT;
fp@291:             eoe->slave->error_flag = 0;
fp@251:             eoe->slave = NULL;
fp@251:         }
fp@251:     }
fp@251: 
fp@251:     master->eoe_running = 0;
fp@251: }
fp@251: 
fp@251: /*****************************************************************************/
fp@251: /**
fp@197:    Does the Ethernet-over-EtherCAT processing.
fp@197: */
fp@197: 
fp@251: void ec_master_eoe_run(unsigned long data /**< master pointer */)
fp@206: {
fp@206:     ec_master_t *master = (ec_master_t *) data;
fp@197:     ec_eoe_t *eoe;
fp@235:     unsigned int active = 0;
fp@344:     cycles_t cycles_start, cycles_end;
fp@344:     unsigned long restart_jiffies;
fp@235: 
fp@251:     list_for_each_entry(eoe, &master->eoe_handlers, list) {
fp@235:         if (ec_eoe_active(eoe)) active++;
fp@235:     }
fp@251:     if (!active) goto queue_timer;
fp@251: 
fp@251:     // aquire master lock...
fp@325:     if (master->mode == EC_MASTER_MODE_OPERATION) {
fp@251:         // request_cb must return 0, if the lock has been aquired!
fp@251:         if (master->request_cb(master->cb_data))
fp@251:             goto queue_timer;
fp@251:     }
fp@306:     else if (master->mode == EC_MASTER_MODE_IDLE) {
fp@251:         spin_lock(&master->internal_lock);
fp@251:     }
fp@251:     else
fp@251:         goto queue_timer;
fp@251: 
fp@344:     // actual EoE processing
fp@344:     cycles_start = get_cycles();
fp@325:     ecrt_master_receive(master);
fp@334: 
fp@251:     list_for_each_entry(eoe, &master->eoe_handlers, list) {
fp@251:         ec_eoe_run(eoe);
fp@251:     }
fp@334: 
fp@325:     ecrt_master_send(master);
fp@344:     cycles_end = get_cycles();
fp@251: 
fp@251:     // release lock...
fp@325:     if (master->mode == EC_MASTER_MODE_OPERATION) {
fp@228:         master->release_cb(master->cb_data);
fp@228:     }
fp@306:     else if (master->mode == EC_MASTER_MODE_IDLE) {
fp@251:         spin_unlock(&master->internal_lock);
fp@251:     }
fp@251: 
fp@335:     master->eoe_cycle_times[master->eoe_cycle_time_pos]
fp@344:         = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz;
fp@335:     master->eoe_cycle_time_pos++;
fp@335:     master->eoe_cycle_time_pos %= HZ;
fp@334: 
fp@251:  queue_timer:
fp@344:     restart_jiffies = HZ / EC_EOE_FREQUENCY;
fp@344:     if (!restart_jiffies) restart_jiffies = 1;
fp@344:     master->eoe_timer.expires += restart_jiffies;
fp@208:     add_timer(&master->eoe_timer);
fp@197: }
fp@197: 
fp@325: /*****************************************************************************/
fp@325: 
fp@325: /**
fp@325:    Calculates Advanced Position Adresses.
fp@325: */
fp@325: 
fp@325: void ec_master_calc_addressing(ec_master_t *master /**< EtherCAT master */)
fp@325: {
fp@325:     uint16_t coupler_index, coupler_subindex;
fp@325:     uint16_t reverse_coupler_index, current_coupler_index;
fp@325:     ec_slave_t *slave;
fp@325: 
fp@325:     coupler_index = 0;
fp@325:     reverse_coupler_index = 0xFFFF;
fp@325:     current_coupler_index = 0x0000;
fp@325:     coupler_subindex = 0;
fp@325: 
fp@325:     list_for_each_entry(slave, &master->slaves, list) {
fp@325:         if (ec_slave_is_coupler(slave)) {
fp@325:             if (slave->sii_alias)
fp@325:                 current_coupler_index = reverse_coupler_index--;
fp@325:             else
fp@325:                 current_coupler_index = coupler_index++;
fp@325:             coupler_subindex = 0;
fp@325:         }
fp@325: 
fp@325:         slave->coupler_index = current_coupler_index;
fp@325:         slave->coupler_subindex = coupler_subindex;
fp@325:         coupler_subindex++;
fp@325:     }
fp@325: }
fp@325: 
fp@330: /*****************************************************************************/
fp@330: 
fp@330: /**
fp@330:    Measures the time, a frame is on the bus.
fp@330: */
fp@330: 
fp@330: void ec_master_measure_bus_time(ec_master_t *master)
fp@330: {
fp@330:     ec_datagram_t datagram;
fp@344:     cycles_t cycles_start, cycles_end, cycles_timeout;
fp@330:     uint32_t times[100], sum, min, max, i;
fp@330: 
fp@330:     ec_datagram_init(&datagram);
fp@330: 
fp@330:     if (ec_datagram_brd(&datagram, 0x130, 2)) {
fp@330:         EC_ERR("Failed to allocate datagram for bus time measuring.\n");
fp@330:         ec_datagram_clear(&datagram);
fp@330:         return;
fp@330:     }
fp@330: 
fp@344:     cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
fp@330: 
fp@330:     sum = 0;
fp@330:     min = 0xFFFFFFFF;
fp@330:     max = 0;
fp@330: 
fp@330:     for (i = 0; i < 100; i++) {
fp@330:         ec_master_queue_datagram(master, &datagram);
fp@330:         ecrt_master_send(master);
fp@344:         cycles_start = get_cycles();
fp@330: 
fp@330:         while (1) { // active waiting
fp@330:             ec_device_call_isr(master->device);
fp@344:             cycles_end = get_cycles(); // take current time
fp@330: 
fp@330:             if (datagram.state == EC_DATAGRAM_RECEIVED) {
fp@330:                 break;
fp@330:             }
fp@330:             else if (datagram.state == EC_DATAGRAM_ERROR) {
fp@330:                 EC_WARN("Failed to measure bus time.\n");
fp@330:                 goto error;
fp@330:             }
fp@344:             else if (cycles_end - cycles_start >= cycles_timeout) {
fp@330:                 EC_WARN("Timeout while measuring bus time.\n");
fp@330:                 goto error;
fp@330:             }
fp@330:         }
fp@330: 
fp@344:         times[i] = (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz;
fp@330:         sum += times[i];
fp@330:         if (times[i] > max) max = times[i];
fp@330:         if (times[i] < min) min = times[i];
fp@330:     }
fp@330: 
fp@330:     EC_INFO("Bus time is (min/avg/max) %u/%u.%u/%u us.\n",
fp@330:             min, sum / 100, sum % 100, max);
fp@330: 
fp@330:   error:
fp@330:     // Dequeue and free datagram
fp@330:     list_del(&datagram.queue);
fp@330:     ec_datagram_clear(&datagram);
fp@330: }
fp@330: 
fp@54: /******************************************************************************
fp@195:  *  Realtime interface
fp@54:  *****************************************************************************/
fp@54: 
fp@54: /**
fp@195:    Creates a domain.
fp@195:    \return pointer to new domain on success, else NULL
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@195: ec_domain_t *ecrt_master_create_domain(ec_master_t *master /**< master */)
fp@73: {
fp@178:     ec_domain_t *domain, *last_domain;
fp@178:     unsigned int index;
fp@73: 
fp@73:     if (!(domain = (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
fp@84:         EC_ERR("Error allocating domain memory!\n");
fp@178:         goto out_return;
fp@178:     }
fp@178: 
fp@178:     if (list_empty(&master->domains)) index = 0;
fp@178:     else {
fp@178:         last_domain = list_entry(master->domains.prev, ec_domain_t, list);
fp@178:         index = last_domain->index + 1;
fp@178:     }
fp@178: 
fp@178:     if (ec_domain_init(domain, master, index)) {
fp@178:         EC_ERR("Failed to init domain.\n");
fp@178:         goto out_return;
fp@178:     }
fp@178: 
fp@178:     if (kobject_add(&domain->kobj)) {
fp@178:         EC_ERR("Failed to add domain kobject.\n");
fp@178:         goto out_put;
fp@178:     }
fp@178: 
fp@95:     list_add_tail(&domain->list, &master->domains);
fp@73:     return domain;
fp@178: 
fp@178:  out_put:
fp@178:     kobject_put(&domain->kobj);
fp@178:  out_return:
fp@178:     return NULL;
fp@73: }
fp@61: 
fp@74: /*****************************************************************************/
fp@74: 
fp@61: /**
fp@195:    Configures all slaves and leads them to the OP state.
fp@195:    Does the complete configuration and activation for all slaves. Sets sync
fp@195:    managers and FMMUs, and does the appropriate transitions, until the slave
fp@195:    is operational.
fp@195:    \return 0 in case of success, else < 0
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@195: int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */)
fp@73: {
fp@73:     uint32_t domain_offset;
fp@84:     ec_domain_t *domain;
fp@325:     ec_fsm_t *fsm = &master->fsm;
fp@325:     ec_slave_t *slave;
fp@144: 
fp@195:     // allocate all domains
fp@73:     domain_offset = 0;
fp@95:     list_for_each_entry(domain, &master->domains, list) {
fp@73:         if (ec_domain_alloc(domain, domain_offset)) {
fp@95:             EC_ERR("Failed to allocate domain %X!\n", (u32) domain);
fp@73:             return -1;
fp@73:         }
fp@73:         domain_offset += domain->data_size;
fp@73:     }
fp@73: 
fp@325:     // determine initial states.
fp@182:     list_for_each_entry(slave, &master->slaves, list) {
fp@325:         if (ec_slave_is_coupler(slave) || slave->registered) {
fp@325:             slave->requested_state = EC_SLAVE_STATE_OP;
fp@325:         }
fp@325:         else {
fp@325:             slave->requested_state = EC_SLAVE_STATE_PREOP;
fp@325:         }
fp@325:     }
fp@325: 
fp@325:     ec_fsm_configuration(fsm); // init configuration state machine
fp@325: 
fp@325:     do {
fp@325:         ec_fsm_execute(fsm);
fp@325:         ec_master_sync_io(master);
fp@325:     }
fp@325:     while (ec_fsm_configuration_running(fsm));
fp@325: 
fp@325:     if (!ec_fsm_configuration_success(fsm)) {
fp@325:         return -1;
fp@325:     }
fp@325: 
fp@325:     ec_fsm_reset(&master->fsm); // prepare for operation state machine
fp@73: 
fp@73:     return 0;
fp@73: }
fp@73: 
fp@73: /*****************************************************************************/
fp@73: 
fp@73: /**
fp@195:    Resets all slaves to INIT state.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@195: void ecrt_master_deactivate(ec_master_t *master /**< EtherCAT master */)
fp@73: {
fp@325:     ec_fsm_t *fsm = &master->fsm;
fp@73:     ec_slave_t *slave;
fp@182: 
fp@182:     list_for_each_entry(slave, &master->slaves, list) {
fp@325:         slave->requested_state = EC_SLAVE_STATE_INIT;
fp@325:     }
fp@325: 
fp@325:     ec_fsm_configuration(fsm); // init configuration state machine
fp@325: 
fp@325:     do {
fp@325:         ec_fsm_execute(fsm);
fp@325:         ec_master_sync_io(master);
fp@325:     }
fp@325:     while (ec_fsm_configuration_running(fsm));
fp@135: }
fp@135: 
fp@104: /*****************************************************************************/
fp@104: 
fp@104: /**
fp@293:    Sends queued datagrams and waits for their reception.
fp@325: */
fp@325: 
fp@325: void ec_master_sync_io(ec_master_t *master /**< EtherCAT master */)
fp@98: {
fp@345:     ec_datagram_t *datagram;
fp@345:     unsigned int datagrams_waiting;
fp@98: 
fp@293:     // send datagrams
fp@325:     ecrt_master_send(master);
fp@98: 
fp@195:     while (1) { // active waiting
fp@345:         ecrt_master_receive(master); // receive and dequeue datagrams
fp@345: 
fp@345:         // count number of datagrams still waiting for response
fp@345:         datagrams_waiting = 0;
fp@345:         list_for_each_entry(datagram, &master->datagram_queue, queue) {
fp@345:             datagrams_waiting++;
fp@345:         }
fp@345: 
fp@345:         // if there are no more datagrams waiting, abort loop.
fp@345:         if (!datagrams_waiting) break;
fp@293:     }
fp@293: }
fp@293: 
fp@293: /*****************************************************************************/
fp@293: 
fp@293: /**
fp@293:    Asynchronous sending of datagrams.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@325: void ecrt_master_send(ec_master_t *master /**< EtherCAT master */)
fp@104: {
fp@293:     ec_datagram_t *datagram, *n;
fp@104: 
fp@104:     if (unlikely(!master->device->link_state)) {
fp@293:         // link is down, no datagram can be sent
fp@293:         list_for_each_entry_safe(datagram, n, &master->datagram_queue, queue) {
fp@325:             datagram->state = EC_DATAGRAM_ERROR;
fp@293:             list_del_init(&datagram->queue);
fp@104:         }
fp@104: 
fp@195:         // query link state
fp@104:         ec_device_call_isr(master->device);
fp@104:         return;
fp@104:     }
fp@104: 
fp@195:     // send frames
fp@293:     ec_master_send_datagrams(master);
fp@293: }
fp@293: 
fp@293: /*****************************************************************************/
fp@293: 
fp@293: /**
fp@293:    Asynchronous receiving of datagrams.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@325: void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */)
fp@104: {
fp@293:     ec_datagram_t *datagram, *next;
fp@344:     cycles_t cycles_received, cycles_timeout;
fp@104: 
fp@104:     ec_device_call_isr(master->device);
fp@104: 
fp@344:     cycles_received = get_cycles();
fp@344:     cycles_timeout = EC_IO_TIMEOUT * cpu_khz / 1000;
fp@208: 
fp@293:     // dequeue all datagrams that timed out
fp@293:     list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
fp@293:         switch (datagram->state) {
fp@325:             case EC_DATAGRAM_SENT:
fp@325:             case EC_DATAGRAM_QUEUED:
fp@344:                 if (cycles_received - datagram->cycles_sent > cycles_timeout) {
fp@293:                     list_del_init(&datagram->queue);
fp@325:                     datagram->state = EC_DATAGRAM_TIMED_OUT;
fp@208:                     master->stats.timeouts++;
fp@208:                     ec_master_output_stats(master);
fp@208:                 }
fp@104:                 break;
fp@104:             default:
fp@104:                 break;
fp@104:         }
fp@144:     }
fp@104: }
fp@104: 
fp@104: /*****************************************************************************/
fp@104: 
fp@104: /**
fp@195:    Prepares synchronous IO.
fp@293:    Queues all domain datagrams and sends them. Then waits a certain time, so
fp@325:    that ecrt_master_receive() can be called securely.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@325: void ecrt_master_prepare(ec_master_t *master /**< EtherCAT master */)
fp@106: {
fp@106:     ec_domain_t *domain;
fp@344:     cycles_t cycles_start, cycles_end, cycles_timeout;
fp@106: 
fp@293:     // queue datagrams of all domains
fp@106:     list_for_each_entry(domain, &master->domains, list)
fp@325:         ec_domain_queue(domain);
fp@325: 
fp@325:     ecrt_master_send(master);
fp@106: 
fp@344:     cycles_start = get_cycles(); // take sending time
fp@344:     cycles_timeout = (cycles_t) EC_IO_TIMEOUT * (cpu_khz / 1000);
fp@106: 
fp@195:     // active waiting
fp@113:     while (1) {
fp@344:         cycles_end = get_cycles();
fp@344:         if (cycles_end - cycles_start >= cycles_timeout) break;
fp@113:     }
fp@106: }
fp@106: 
fp@106: /*****************************************************************************/
fp@106: 
fp@106: /**
fp@195:    Does all cyclic master work.
fp@199:    \ingroup RealtimeInterface
fp@195: */
fp@195: 
fp@195: void ecrt_master_run(ec_master_t *master /**< EtherCAT master */)
fp@195: {
fp@195:     // output statistics
fp@144:     ec_master_output_stats(master);
fp@144: 
fp@238:     // execute master state machine
fp@238:     ec_fsm_execute(&master->fsm);
fp@144: }
fp@144: 
fp@144: /*****************************************************************************/
fp@144: 
fp@144: /**
fp@197:    Translates an ASCII coded bus-address to a slave pointer.
fp@197:    These are the valid addressing schemes:
fp@197:    - \a "X" = the X. slave on the bus,
fp@197:    - \a "X:Y" = the Y. slave after the X. branch (bus coupler),
fp@197:    - \a "#X" = the slave with alias X,
fp@197:    - \a "#X:Y" = the Y. slave after the branch (bus coupler) with alias X.
fp@197:    X and Y are zero-based indices and may be provided in hexadecimal or octal
fp@197:    notation (with respective prefix).
fp@197:    \return pointer to the slave on success, else NULL
fp@199:    \ingroup RealtimeInterface
fp@197: */
fp@197: 
fp@197: ec_slave_t *ecrt_master_get_slave(const ec_master_t *master, /**< Master */
fp@197:                                   const char *address /**< address string */
fp@197:                                   )
fp@197: {
fp@197:     unsigned long first, second;
fp@197:     char *remainder, *remainder2;
fp@197:     unsigned int alias_requested, alias_found;
fp@197:     ec_slave_t *alias_slave = NULL, *slave;
fp@197: 
fp@197:     if (!address || address[0] == 0) return NULL;
fp@197: 
fp@197:     alias_requested = 0;
fp@197:     if (address[0] == '#') {
fp@197:         alias_requested = 1;
fp@197:         address++;
fp@197:     }
fp@197: 
fp@197:     first = simple_strtoul(address, &remainder, 0);
fp@197:     if (remainder == address) {
fp@197:         EC_ERR("Slave address \"%s\" - First number empty!\n", address);
fp@197:         return NULL;
fp@197:     }
fp@197: 
fp@197:     if (alias_requested) {
fp@197:         alias_found = 0;
fp@197:         list_for_each_entry(alias_slave, &master->slaves, list) {
fp@197:             if (alias_slave->sii_alias == first) {
fp@197:                 alias_found = 1;
fp@197:                 break;
fp@197:             }
fp@197:         }
fp@197:         if (!alias_found) {
fp@197:             EC_ERR("Slave address \"%s\" - Alias not found!\n", address);
fp@197:             return NULL;
fp@197:         }
fp@197:     }
fp@197: 
fp@197:     if (!remainder[0]) { // absolute position
fp@197:         if (alias_requested) {
fp@197:             return alias_slave;
fp@197:         }
fp@197:         else {
fp@197:             list_for_each_entry(slave, &master->slaves, list) {
fp@197:                 if (slave->ring_position == first) return slave;
fp@197:             }
fp@197:             EC_ERR("Slave address \"%s\" - Absolute position invalid!\n",
fp@197:                    address);
fp@197:         }
fp@197:     }
fp@197:     else if (remainder[0] == ':') { // field position
fp@197:         remainder++;
fp@197:         second = simple_strtoul(remainder, &remainder2, 0);
fp@197: 
fp@197:         if (remainder2 == remainder) {
fp@197:             EC_ERR("Slave address \"%s\" - Second number empty!\n", address);
fp@197:             return NULL;
fp@197:         }
fp@197: 
fp@197:         if (remainder2[0]) {
fp@197:             EC_ERR("Slave address \"%s\" - Invalid trailer!\n", address);
fp@197:             return NULL;
fp@197:         }
fp@197: 
fp@197:         if (alias_requested) {
fp@325:             if (!ec_slave_is_coupler(alias_slave)) {
fp@197:                 EC_ERR("Slave address \"%s\": Alias slave must be bus coupler"
fp@197:                        " in colon mode.\n", address);
fp@197:                 return NULL;
fp@197:             }
fp@197:             list_for_each_entry(slave, &master->slaves, list) {
fp@197:                 if (slave->coupler_index == alias_slave->coupler_index
fp@197:                     && slave->coupler_subindex == second)
fp@197:                     return slave;
fp@197:             }
fp@197:             EC_ERR("Slave address \"%s\" - Bus coupler %i has no %lu. slave"
fp@197:                    " following!\n", address, alias_slave->ring_position,
fp@197:                    second);
fp@197:             return NULL;
fp@197:         }
fp@197:         else {
fp@197:             list_for_each_entry(slave, &master->slaves, list) {
fp@197:                 if (slave->coupler_index == first
fp@197:                     && slave->coupler_subindex == second) return slave;
fp@197:             }
fp@197:         }
fp@197:     }
fp@197:     else
fp@197:         EC_ERR("Slave address \"%s\" - Invalid format!\n", address);
fp@197: 
fp@197:     return NULL;
fp@197: }
fp@197: 
fp@197: /*****************************************************************************/
fp@197: 
fp@197: /**
fp@204:    Sets the locking callbacks.
fp@226:    The request_cb function must return zero, to allow another instance
fp@226:    (the EoE process for example) to access the master. Non-zero means,
fp@226:    that access is forbidden at this time.
fp@204:    \ingroup RealtimeInterface
fp@204: */
fp@204: 
fp@204: void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */
fp@204:                            int (*request_cb)(void *), /**< request lock CB */
fp@204:                            void (*release_cb)(void *), /**< release lock CB */
fp@204:                            void *cb_data /**< data parameter */
fp@204:                            )
fp@204: {
fp@204:     master->request_cb = request_cb;
fp@204:     master->release_cb = release_cb;
fp@204:     master->cb_data = cb_data;
fp@204: }
fp@204: 
fp@204: /*****************************************************************************/
fp@204: 
fp@199: /** \cond */
fp@199: 
fp@104: EXPORT_SYMBOL(ecrt_master_create_domain);
fp@104: EXPORT_SYMBOL(ecrt_master_activate);
fp@104: EXPORT_SYMBOL(ecrt_master_deactivate);
fp@325: EXPORT_SYMBOL(ecrt_master_prepare);
fp@325: EXPORT_SYMBOL(ecrt_master_send);
fp@325: EXPORT_SYMBOL(ecrt_master_receive);
fp@144: EXPORT_SYMBOL(ecrt_master_run);
fp@206: EXPORT_SYMBOL(ecrt_master_callbacks);
fp@138: EXPORT_SYMBOL(ecrt_master_get_slave);
fp@42: 
fp@199: /** \endcond */
fp@199: 
fp@199: /*****************************************************************************/