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 fp@0: #include fp@0: #include fp@0: #include fp@0: #include 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@404: void ec_master_clear(struct kobject *); 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@441: void ec_master_check_sdo(unsigned long); fp@446: int ec_master_measure_bus_time(ec_master_t *); 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@446: atomic_set(&master->available, 1); fp@178: master->index = index; fp@446: fp@98: master->device = NULL; fp@381: init_MUTEX(&master->device_sem); fp@446: fp@446: master->mode = EC_MASTER_MODE_ORPHANED; fp@446: fp@182: INIT_LIST_HEAD(&master->slaves); fp@446: master->slave_count = 0; fp@446: fp@293: INIT_LIST_HEAD(&master->datagram_queue); fp@446: master->datagram_index = 0; fp@446: fp@95: INIT_LIST_HEAD(&master->domains); fp@446: master->debug_level = 0; fp@446: fp@446: master->stats.timeouts = 0; fp@446: master->stats.corrupted = 0; fp@446: master->stats.skipped = 0; fp@446: master->stats.unmatched = 0; fp@446: master->stats.output_jiffies = 0; fp@446: fp@318: INIT_WORK(&master->idle_work, ec_master_idle_run, (void *) master); fp@446: fp@446: for (i = 0; i < HZ; i++) { fp@446: master->idle_cycle_times[i] = 0; fp@446: master->eoe_cycle_times[i] = 0; fp@446: } fp@446: master->idle_cycle_time_pos = 0; fp@446: master->eoe_cycle_time_pos = 0; fp@446: 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->eoe_running = 0; fp@326: master->eoe_checked = 0; fp@446: INIT_LIST_HEAD(&master->eoe_handlers); fp@446: fp@446: master->internal_lock = SPIN_LOCK_UNLOCKED; fp@446: master->request_cb = NULL; fp@446: master->release_cb = NULL; fp@446: master->cb_data = NULL; fp@446: fp@446: master->eeprom_write_enable = 0; fp@446: fp@446: master->sdo_request = NULL; fp@446: master->sdo_seq_user = 0; fp@446: master->sdo_seq_master = 0; fp@441: init_MUTEX(&master->sdo_sem); fp@441: init_timer(&master->sdo_timer); fp@441: master->sdo_timer.function = ec_master_check_sdo; fp@441: master->sdo_timer.data = (unsigned long) master; fp@441: init_completion(&master->sdo_complete); 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@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@446: ec_datagram_t *datagram, *next_c; fp@446: fp@446: ec_master_clear_slaves(master); fp@446: fp@446: // empty datagram queue fp@446: list_for_each_entry_safe(datagram, next_c, fp@446: &master->datagram_queue, queue) { fp@446: datagram->state = EC_DATAGRAM_ERROR; fp@446: list_del_init(&datagram->queue); fp@446: } fp@446: 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@178: EC_INFO("Master %i cleared.\n", master->index); fp@418: fp@418: kfree(master); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** 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@446: fp@238: master->slave_count = 0; fp@74: } fp@74: fp@74: /*****************************************************************************/ fp@74: fp@74: /** fp@430: Flushes the SDO request queue. fp@430: */ fp@430: fp@430: void ec_master_flush_sdo_requests(ec_master_t *master) fp@430: { fp@441: del_timer_sync(&master->sdo_timer); fp@441: complete(&master->sdo_complete); fp@441: master->sdo_request = NULL; fp@441: master->sdo_seq_user = 0; fp@441: master->sdo_seq_master = 0; fp@430: } fp@430: fp@430: /*****************************************************************************/ fp@430: fp@430: /** fp@446: */ fp@446: fp@446: int ec_master_enter_idle_mode(ec_master_t *master /**< EtherCAT master */) fp@446: { fp@446: master->mode = EC_MASTER_MODE_IDLE; fp@446: queue_delayed_work(master->workqueue, &master->idle_work, 1); fp@446: return 0; fp@446: } fp@446: fp@446: /*****************************************************************************/ fp@446: fp@446: /** fp@446: */ fp@446: fp@446: void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */) fp@446: { fp@446: ec_master_eoe_stop(master); fp@446: fp@446: master->mode = EC_MASTER_MODE_ORPHANED; fp@446: if (!cancel_delayed_work(&master->idle_work)) { fp@446: flush_workqueue(master->workqueue); fp@446: } fp@446: fp@446: ec_master_flush_sdo_requests(master); fp@446: } fp@446: fp@446: /*****************************************************************************/ fp@446: fp@446: /** fp@446: */ fp@446: fp@446: int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */) fp@446: { fp@446: ec_slave_t *slave; fp@446: ec_datagram_t *datagram = &master->fsm.datagram; fp@446: fp@446: master->mode = EC_MASTER_MODE_OPERATION; fp@446: if (!cancel_delayed_work(&master->idle_work)) { fp@446: flush_workqueue(master->workqueue); fp@446: } fp@446: fp@446: // wait for FSM datagram fp@446: if (datagram->state == EC_DATAGRAM_SENT) {; fp@446: while (get_cycles() - datagram->cycles_sent fp@446: < (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) {} fp@446: ecrt_master_receive(master); fp@446: } fp@446: fp@446: // finish running FSM fp@446: while (ec_fsm_exec(&master->fsm)) { fp@446: ec_master_sync_io(master); fp@446: } fp@446: fp@446: if (!ec_fsm_success(&master->fsm)) { fp@446: EC_ERR("Master state machine failure!\n"); fp@446: goto out_idle; fp@446: } fp@446: fp@446: if (master->debug_level) { fp@446: if (ec_master_measure_bus_time(master)) { fp@446: EC_ERR("Bus time measuring failed!\n"); fp@446: goto out_idle; fp@446: } fp@446: } fp@446: fp@446: // set initial slave states fp@446: list_for_each_entry(slave, &master->slaves, list) { fp@446: if (ec_slave_is_coupler(slave)) { fp@446: ec_slave_request_state(slave, EC_SLAVE_STATE_OP); fp@446: } fp@446: else { fp@446: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); fp@446: } fp@446: } fp@446: fp@446: return 0; fp@446: fp@446: out_idle: fp@446: master->mode = EC_MASTER_MODE_IDLE; fp@446: queue_delayed_work(master->workqueue, &master->idle_work, 1); fp@446: return -1; fp@446: } fp@446: fp@446: /*****************************************************************************/ fp@446: fp@446: /** fp@446: */ fp@446: fp@446: void ec_master_leave_operation_mode(ec_master_t *master fp@446: /**< EtherCAT master */) fp@446: { fp@446: ec_slave_t *slave; fp@446: ec_domain_t *domain, *next_d; fp@446: fp@446: // clear domains fp@446: list_for_each_entry_safe(domain, next_d, &master->domains, list) { fp@446: ec_domain_dequeue_datagrams(domain); fp@446: list_del(&domain->list); fp@446: kobject_del(&domain->kobj); fp@446: kobject_put(&domain->kobj); fp@446: } fp@446: fp@446: master->request_cb = NULL; fp@446: master->release_cb = NULL; fp@446: master->cb_data = NULL; fp@446: fp@446: list_for_each_entry(slave, &master->slaves, list) { fp@446: ec_slave_reset(slave); fp@446: } fp@446: fp@446: master->mode = EC_MASTER_MODE_IDLE; fp@446: queue_delayed_work(master->workqueue, &master->idle_work, 1); fp@446: } fp@446: fp@446: /*****************************************************************************/ fp@446: fp@446: /** 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@398: datagram->cycles_queued = get_cycles(); 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@398: ec_datagram_t *datagram, *next; fp@293: size_t datagram_size; fp@98: uint8_t *frame_data, *cur_data; fp@98: void *follows_word; fp@398: cycles_t cycles_start, cycles_sent, cycles_end; fp@398: unsigned long jiffies_sent; fp@293: unsigned int frame_count, more_datagrams_waiting; fp@398: struct list_head sent_datagrams; fp@398: fp@398: cycles_start = get_cycles(); fp@176: frame_count = 0; fp@398: INIT_LIST_HEAD(&sent_datagrams); 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@398: list_add_tail(&datagram->sent, &sent_datagrams); 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@398: if (list_empty(&sent_datagrams)) { 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@398: cycles_sent = get_cycles(); fp@398: jiffies_sent = jiffies; fp@398: fp@398: // set datagram states and sending timestamps fp@398: list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) { fp@398: datagram->state = EC_DATAGRAM_SENT; fp@398: datagram->cycles_sent = cycles_sent; fp@398: datagram->jiffies_sent = jiffies_sent; fp@398: list_del_init(&datagram->sent); // empty list of sent datagrams fp@398: } fp@398: 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@398: datagram->cycles_received = master->device->cycles_isr; fp@398: datagram->jiffies_received = master->device->jiffies_isr; fp@293: list_del_init(&datagram->queue); fp@293: } fp@293: } fp@293: fp@293: /*****************************************************************************/ fp@293: fp@293: /** 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@396: EC_WARN("%i datagram%s TIMED OUT!\n", master->stats.timeouts, fp@396: master->stats.timeouts == 1 ? "" : "s"); fp@98: master->stats.timeouts = 0; fp@98: } fp@98: if (master->stats.corrupted) { fp@396: EC_WARN("%i frame%s CORRUPTED!\n", master->stats.corrupted, fp@396: master->stats.corrupted == 1 ? "" : "s"); fp@98: master->stats.corrupted = 0; fp@98: } fp@332: if (master->stats.skipped) { fp@396: EC_WARN("%i datagram%s SKIPPED!\n", master->stats.skipped, fp@396: master->stats.skipped == 1 ? "" : "s"); fp@332: master->stats.skipped = 0; fp@332: } fp@98: if (master->stats.unmatched) { fp@396: EC_WARN("%i datagram%s UNMATCHED!\n", master->stats.unmatched, fp@396: master->stats.unmatched == 1 ? "" : "s"); fp@98: master->stats.unmatched = 0; fp@98: } fp@73: } fp@54: } fp@54: fp@68: /*****************************************************************************/ fp@68: fp@68: /** 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@435: ec_fsm_exec(&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@414: EC_DBG("Slave %3i, SM %i: Addr 0x%04X, Size %3i, Ctrl 0x%02X, En %i\n", fp@413: slave->ring_position, sync->index, sync->physical_start_address, fp@414: sync_size, sync->control_register, sync->enable); 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@412: if (slave->master->debug_level) { fp@413: EC_DBG("Slave %3i, FMMU %2i:" fp@413: " LogAddr 0x%08X, Size %3i, PhysAddr 0x%04X, Dir %s\n", fp@413: slave->ring_position, fmmu->index, fmmu->logical_start_address, fp@413: sync_size, fmmu->sync->physical_start_address, fp@413: ((fmmu->sync->control_register & 0x04) ? "out" : "in")); fp@412: } fp@412: 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@444: off += sprintf(buffer + off, "\nVersion: %s", ec_master_version_str); 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@446: if (eoe->opened) fp@446: ec_slave_request_state(slave, EC_SLAVE_STATE_OP); fp@446: else fp@446: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); 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@446: ec_slave_request_state(slave, EC_SLAVE_STATE_INIT); 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@446: ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP); fp@251: eoe->slave = NULL; fp@251: } fp@251: } fp@251: fp@251: master->eoe_running = 0; fp@251: } fp@251: fp@251: /*****************************************************************************/ fp@441: 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@441: */ fp@441: fp@441: void ec_master_check_sdo(unsigned long data /**< master pointer */) fp@441: { fp@441: ec_master_t *master = (ec_master_t *) data; fp@441: fp@441: if (master->sdo_seq_master != master->sdo_seq_user) { fp@441: master->sdo_timer.expires = jiffies + 10; fp@441: add_timer(&master->sdo_timer); fp@441: return; fp@441: } fp@441: fp@441: // master has processed the request fp@441: complete(&master->sdo_complete); fp@441: } fp@441: fp@441: /*****************************************************************************/ fp@441: fp@441: /** 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@377: \return 0 in case of success, else < 0 fp@377: */ fp@377: fp@377: int ec_master_measure_bus_time(ec_master_t *master) fp@330: { fp@330: ec_datagram_t datagram; fp@398: uint32_t cur, 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@377: return -1; fp@330: } fp@330: fp@398: ecrt_master_receive(master); 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@398: fp@398: while (1) { fp@398: ecrt_master_receive(master); 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@398: else if (datagram.state == EC_DATAGRAM_TIMED_OUT) { fp@330: EC_WARN("Timeout while measuring bus time.\n"); fp@330: goto error; fp@330: } fp@330: } fp@330: fp@398: cur = (unsigned int) (datagram.cycles_received fp@398: - datagram.cycles_sent) * 1000 / cpu_khz; fp@398: sum += cur; fp@398: if (cur > max) max = cur; fp@398: if (cur < min) min = cur; fp@398: } fp@398: fp@398: EC_INFO("Bus time is (min/avg/max) %u / %u.%u / %u us.\n", fp@330: min, sum / 100, sum % 100, max); fp@398: ec_datagram_clear(&datagram); fp@377: return 0; fp@330: fp@330: error: fp@330: ec_datagram_clear(&datagram); fp@377: return -1; 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@446: // execute master FSM until termination fp@435: while (ec_fsm_exec(fsm)) { fp@325: ec_master_sync_io(master); fp@325: } fp@325: fp@446: if (!ec_fsm_success(fsm)) { fp@446: EC_ERR("Error in master state machine.\n"); fp@325: return -1; fp@325: } fp@325: fp@446: // check, if all slaves have been configured fp@446: list_for_each_entry(slave, &master->slaves, list) { fp@446: if (slave->error_flag || !slave->online) { fp@446: EC_ERR("Failed to configure slave %i!\n", slave->ring_position); fp@446: return -1; fp@446: } fp@446: } 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@446: ec_datagram_t *datagram = &master->fsm.datagram; fp@446: fp@446: // wait for FSM datagram fp@446: if (datagram->state == EC_DATAGRAM_SENT) { fp@446: while (get_cycles() - datagram->cycles_sent fp@446: < (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) {} fp@446: ecrt_master_receive(master); fp@446: } fp@446: fp@446: // set states for all slaves fp@182: list_for_each_entry(slave, &master->slaves, list) { fp@446: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); fp@446: } fp@446: fp@446: // execute master FSM to deactivate slaves fp@435: while (ec_fsm_exec(fsm)) { fp@325: ec_master_sync_io(master); fp@325: } 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@398: cycles_t cycles_timeout; fp@398: fp@398: // receive datagrams fp@104: ec_device_call_isr(master->device); fp@104: fp@398: cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (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@398: case EC_DATAGRAM_QUEUED: fp@398: if (master->device->cycles_isr fp@398: - datagram->cycles_queued > cycles_timeout) { fp@398: list_del_init(&datagram->queue); fp@398: datagram->state = EC_DATAGRAM_TIMED_OUT; fp@398: master->stats.timeouts++; fp@398: ec_master_output_stats(master); fp@398: } fp@398: break; fp@325: case EC_DATAGRAM_SENT: fp@398: if (master->device->cycles_isr fp@398: - 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@446: ec_domain_queue_datagrams(domain); fp@325: fp@325: ecrt_master_send(master); fp@106: fp@398: cycles_start = get_cycles(); fp@398: cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); fp@106: fp@195: // active waiting fp@113: while (1) { fp@398: udelay(100); 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@446: // execute master state machine in a loop fp@435: ec_fsm_exec(&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: /*****************************************************************************/