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@1745: #ifdef EC_EOE fp@145: #include "ethernet.h" fp@1745: #endif fp@0: fp@39: /*****************************************************************************/ fp@0: fp@1732: void ec_master_destroy_domains(ec_master_t *); fp@1744: static int ec_master_idle_thread(ec_master_t *); fp@1744: static int ec_master_operation_thread(ec_master_t *); fp@1745: #ifdef EC_EOE fp@251: void ec_master_eoe_run(unsigned long); fp@1745: #endif 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@1716: EC_SYSFS_READ_ATTR(info); fp@1715: EC_SYSFS_READ_WRITE_ATTR(debug_level); fp@178: fp@178: static struct attribute *ec_def_attrs[] = { fp@1716: &attr_info, fp@1715: &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@1744: .release = NULL, 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@1744: struct kobject *module_kobj, /**< kobject of the master module */ fp@1744: unsigned int index, /**< master index */ fp@1744: const uint8_t *main_mac, /**< MAC address of main device */ fp@1744: const uint8_t *backup_mac /**< MAC address of backup device */ fp@1744: ) fp@1744: { fp@251: unsigned int i; fp@251: fp@178: master->index = index; fp@1744: master->reserved = 0; fp@1744: fp@1744: master->main_mac = main_mac; fp@1744: master->backup_mac = backup_mac; fp@1731: init_MUTEX(&master->device_sem); fp@1732: fp@1732: master->mode = EC_MASTER_MODE_ORPHANED; fp@1744: master->injection_seq_fsm = 0; fp@1744: master->injection_seq_rt = 0; fp@1732: fp@182: INIT_LIST_HEAD(&master->slaves); fp@1732: master->slave_count = 0; fp@1744: fp@1744: master->scan_state = EC_REQUEST_IN_PROGRESS; fp@1744: master->allow_scan = 1; fp@1744: init_MUTEX(&master->scan_sem); fp@1744: init_waitqueue_head(&master->scan_queue); fp@1744: fp@1744: master->config_state = EC_REQUEST_COMPLETE; fp@1744: master->allow_config = 1; fp@1744: init_MUTEX(&master->config_sem); fp@1744: init_waitqueue_head(&master->config_queue); fp@1744: fp@293: INIT_LIST_HEAD(&master->datagram_queue); fp@1732: master->datagram_index = 0; fp@1732: fp@95: INIT_LIST_HEAD(&master->domains); fp@1732: master->debug_level = 0; fp@1732: fp@1732: master->stats.timeouts = 0; fp@1732: master->stats.corrupted = 0; fp@1732: master->stats.unmatched = 0; fp@1732: master->stats.output_jiffies = 0; fp@1732: fp@1732: for (i = 0; i < HZ; i++) { fp@1732: master->idle_cycle_times[i] = 0; fp@1745: #ifdef EC_EOE fp@1732: master->eoe_cycle_times[i] = 0; fp@1745: #endif fp@1732: } fp@1732: master->idle_cycle_time_pos = 0; fp@1745: #ifdef EC_EOE fp@1732: master->eoe_cycle_time_pos = 0; fp@1732: 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@1732: INIT_LIST_HEAD(&master->eoe_handlers); fp@1745: #endif fp@1732: fp@1732: master->internal_lock = SPIN_LOCK_UNLOCKED; fp@1732: master->request_cb = NULL; fp@1732: master->release_cb = NULL; fp@1732: master->cb_data = NULL; fp@1732: fp@1744: INIT_LIST_HEAD(&master->eeprom_requests); fp@1744: init_MUTEX(&master->eeprom_sem); fp@1744: init_waitqueue_head(&master->eeprom_queue); fp@1744: fp@1744: INIT_LIST_HEAD(&master->sdo_requests); fp@1732: init_MUTEX(&master->sdo_sem); fp@1744: init_waitqueue_head(&master->sdo_queue); fp@1744: fp@1744: // init devices fp@1744: if (ec_device_init(&master->main_device, master)) fp@1744: goto out_return; fp@1744: fp@1744: if (ec_device_init(&master->backup_device, master)) fp@1744: goto out_clear_main; fp@251: fp@1739: // init state machine datagram fp@1739: ec_datagram_init(&master->fsm_datagram); fp@1745: snprintf(master->fsm_datagram.name, EC_DATAGRAM_NAME_SIZE, "master-fsm"); fp@1739: if (ec_datagram_prealloc(&master->fsm_datagram, EC_MAX_DATA_SIZE)) { fp@1739: EC_ERR("Failed to allocate FSM datagram.\n"); fp@1744: goto out_clear_backup; fp@1739: } fp@1739: fp@251: // create state machine object fp@1739: ec_fsm_master_init(&master->fsm, master, &master->fsm_datagram); 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@1744: master->kobj.parent = module_kobj; fp@1744: fp@1744: if (kobject_set_name(&master->kobj, "master%i", index)) { fp@1744: EC_ERR("Failed to set master kobject name.\n"); fp@238: kobject_put(&master->kobj); fp@1744: goto out_clear_fsm; fp@1744: } fp@1744: fp@1732: if (kobject_add(&master->kobj)) { fp@1744: EC_ERR("Failed to add master kobject.\n"); fp@1732: kobject_put(&master->kobj); fp@1744: goto out_clear_fsm; fp@1732: } fp@1732: fp@178: return 0; fp@226: fp@1744: out_clear_fsm: fp@1744: ec_fsm_master_clear(&master->fsm); fp@1744: out_clear_backup: fp@1744: ec_device_clear(&master->backup_device); fp@1744: out_clear_main: fp@1744: ec_device_clear(&master->main_device); fp@1744: out_return: fp@1744: return -1; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: Clear and free master. fp@1744: This method is called by the kobject, fp@1744: once there are no more references to it. fp@1744: */ fp@1744: fp@1744: void ec_master_clear( fp@1744: ec_master_t *master /**< EtherCAT master */ fp@1744: ) fp@1744: { fp@1745: #ifdef EC_EOE fp@1744: ec_master_clear_eoe_handlers(master); fp@1745: #endif fp@1744: ec_master_destroy_slaves(master); fp@1744: ec_master_destroy_domains(master); fp@1744: ec_fsm_master_clear(&master->fsm); fp@1744: ec_datagram_clear(&master->fsm_datagram); fp@1744: ec_device_clear(&master->backup_device); fp@1744: ec_device_clear(&master->main_device); fp@1744: fp@1744: // destroy self fp@1744: kobject_del(&master->kobj); fp@1744: kobject_put(&master->kobj); fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1745: #ifdef EC_EOE fp@1744: /** fp@1744: * Clear and free all EoE handlers. fp@1744: */ fp@1744: fp@1744: void ec_master_clear_eoe_handlers( fp@1744: ec_master_t *master /**< EtherCAT master */ fp@1744: ) fp@1744: { fp@1744: ec_eoe_t *eoe, *next; fp@1744: fp@1744: list_for_each_entry_safe(eoe, next, &master->eoe_handlers, list) { fp@251: list_del(&eoe->list); fp@251: ec_eoe_clear(eoe); fp@251: kfree(eoe); fp@251: } fp@1732: } fp@1745: #endif fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: Destroy all slaves. fp@1732: */ fp@1732: fp@1732: void ec_master_destroy_slaves(ec_master_t *master) fp@1732: { fp@1732: ec_slave_t *slave, *next_slave; fp@1732: fp@1732: list_for_each_entry_safe(slave, next_slave, &master->slaves, list) { fp@1732: list_del(&slave->list); fp@1732: ec_slave_destroy(slave); fp@1732: } fp@1732: fp@1732: master->slave_count = 0; fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1732: Destroy all domains. fp@1732: */ fp@1732: fp@1732: void ec_master_destroy_domains(ec_master_t *master) fp@1732: { fp@144: ec_domain_t *domain, *next_d; fp@251: fp@144: list_for_each_entry_safe(domain, next_d, &master->domains, list) { fp@98: list_del(&domain->list); fp@1732: ec_domain_destroy(domain); fp@1732: } fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1739: Internal locking callback. fp@1739: */ fp@1739: fp@1739: int ec_master_request_cb(void *master /**< callback data */) fp@1739: { fp@1739: spin_lock(&((ec_master_t *) master)->internal_lock); fp@1739: return 0; fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@1739: Internal unlocking callback. fp@1739: */ fp@1739: fp@1739: void ec_master_release_cb(void *master /**< callback data */) fp@1739: { fp@1739: spin_unlock(&((ec_master_t *) master)->internal_lock); fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@1739: * Starts the master thread. fp@1744: */ fp@1744: fp@1744: int ec_master_thread_start( fp@1744: ec_master_t *master, /**< EtherCAT master */ fp@1744: int (*thread_func)(ec_master_t *) /**< thread function to start */ fp@1744: ) fp@1739: { fp@1739: init_completion(&master->thread_exit); fp@1744: fp@1739: EC_INFO("Starting master thread.\n"); fp@1744: if (!(master->thread_id = kernel_thread((int (*)(void *)) thread_func, fp@1744: master, CLONE_KERNEL))) fp@1739: return -1; fp@1739: fp@1739: return 0; fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@1739: * Stops the master thread. fp@1744: */ fp@1739: fp@1739: void ec_master_thread_stop(ec_master_t *master /**< EtherCAT master */) fp@1739: { fp@1744: if (!master->thread_id) { fp@1744: EC_WARN("ec_master_thread_stop: Already finished!\n"); fp@1744: return; fp@1744: } fp@1744: fp@1744: kill_proc(master->thread_id, SIGTERM, 1); fp@1744: wait_for_completion(&master->thread_exit); fp@1744: EC_INFO("Master thread exited.\n"); fp@1744: fp@1744: if (master->fsm_datagram.state != EC_DATAGRAM_SENT) return; fp@1744: fp@1744: // wait for FSM datagram fp@1744: while (get_cycles() - master->fsm_datagram.cycles_sent fp@1744: < (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000)) fp@1744: schedule(); fp@1739: } fp@1739: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@1739: * Transition function from ORPHANED to IDLE mode. fp@1744: */ fp@1732: fp@1732: int ec_master_enter_idle_mode(ec_master_t *master /**< EtherCAT master */) fp@1732: { fp@1739: master->request_cb = ec_master_request_cb; fp@1739: master->release_cb = ec_master_release_cb; fp@1739: master->cb_data = master; fp@1744: fp@1732: master->mode = EC_MASTER_MODE_IDLE; fp@1744: if (ec_master_thread_start(master, ec_master_idle_thread)) { fp@1739: master->mode = EC_MASTER_MODE_ORPHANED; fp@1739: return -1; fp@1739: } fp@1739: fp@1732: return 0; fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1739: * Transition function from IDLE to ORPHANED mode. fp@1744: */ fp@1732: fp@1732: void ec_master_leave_idle_mode(ec_master_t *master /**< EtherCAT master */) fp@1732: { fp@1739: master->mode = EC_MASTER_MODE_ORPHANED; fp@1739: fp@1745: #ifdef EC_EOE fp@1732: ec_master_eoe_stop(master); fp@1745: #endif fp@1739: ec_master_thread_stop(master); fp@1744: ec_master_destroy_slaves(master); fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1739: * Transition function from IDLE to OPERATION mode. fp@1744: */ fp@1732: fp@1732: int ec_master_enter_operation_mode(ec_master_t *master /**< EtherCAT master */) fp@1732: { fp@1732: ec_slave_t *slave; fp@1745: #ifdef EC_EOE fp@1744: ec_eoe_t *eoe; fp@1745: #endif fp@1744: fp@1744: down(&master->config_sem); fp@1744: master->allow_config = 0; // temporarily disable slave configuration fp@1744: up(&master->config_sem); fp@1744: fp@1744: // wait for slave configuration to complete fp@1744: if (wait_event_interruptible(master->config_queue, fp@1744: master->config_state != EC_REQUEST_IN_PROGRESS)) { fp@1744: EC_INFO("Finishing slave configuration interrupted by signal.\n"); fp@1744: goto out_allow; fp@1744: } fp@1744: fp@1744: if (master->debug_level) fp@1744: EC_DBG("Waiting for pending slave configuration returned.\n"); fp@1744: fp@1744: down(&master->scan_sem); fp@1744: master->allow_scan = 0; // 'lock' the slave list fp@1744: up(&master->scan_sem); fp@1744: fp@1744: // wait for slave scan to complete fp@1744: if (wait_event_interruptible(master->scan_queue, fp@1744: master->scan_state != EC_REQUEST_IN_PROGRESS)) { fp@1744: EC_INFO("Waiting for slave scan interrupted by signal.\n"); fp@1744: goto out_allow; fp@1744: } fp@1744: fp@1744: if (master->debug_level) fp@1744: EC_DBG("Waiting for pending slave scan returned.\n"); fp@1744: fp@1744: // set states for all slaves fp@1744: list_for_each_entry(slave, &master->slaves, list) { fp@1744: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); fp@1744: } fp@1745: #ifdef EC_EOE fp@1744: // ... but set EoE slaves to OP fp@1744: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1744: if (ec_eoe_is_open(eoe)) fp@1744: ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP); fp@1744: } fp@1745: #endif fp@1744: fp@1744: if (master->debug_level) fp@1744: EC_DBG("Switching to operation mode.\n"); fp@1732: fp@1732: master->mode = EC_MASTER_MODE_OPERATION; fp@1744: master->pdo_slaves_offline = 0; // assume all PDO slaves online fp@1744: master->frames_timed_out = 0; fp@1744: master->ext_request_cb = NULL; fp@1744: master->ext_release_cb = NULL; fp@1744: master->ext_cb_data = NULL; fp@1732: return 0; fp@1744: fp@1744: out_allow: fp@1744: master->allow_scan = 1; fp@1744: master->allow_config = 1; fp@1732: return -1; fp@1732: } fp@1732: fp@1732: /*****************************************************************************/ fp@1732: fp@1732: /** fp@1739: * Transition function from OPERATION to IDLE mode. fp@1744: */ fp@1732: fp@1732: void ec_master_leave_operation_mode(ec_master_t *master fp@1732: /**< EtherCAT master */) fp@1732: { fp@1732: ec_slave_t *slave; fp@1745: #ifdef EC_EOE fp@1744: ec_eoe_t *eoe; fp@1745: #endif fp@1744: fp@1744: master->mode = EC_MASTER_MODE_IDLE; fp@1744: fp@1745: #ifdef EC_EOE fp@1744: ec_master_eoe_stop(master); fp@1745: #endif fp@1744: ec_master_thread_stop(master); fp@1744: fp@1744: master->request_cb = ec_master_request_cb; fp@1744: master->release_cb = ec_master_release_cb; fp@1744: master->cb_data = master; fp@1739: fp@1732: // set states for all slaves fp@1732: list_for_each_entry(slave, &master->slaves, list) { fp@1732: ec_slave_reset(slave); fp@1732: ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP); fp@1744: } fp@1745: #ifdef EC_EOE fp@1744: // ... but leave EoE slaves in OP fp@1744: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1744: if (ec_eoe_is_open(eoe)) fp@1744: ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP); fp@1744: } fp@1745: #endif fp@1732: fp@1732: ec_master_destroy_domains(master); fp@1744: fp@1744: if (ec_master_thread_start(master, ec_master_idle_thread)) fp@1744: EC_WARN("Failed to restart master thread!\n"); fp@1745: #ifdef EC_EOE fp@1744: ec_master_eoe_start(master); fp@1745: #endif fp@1744: fp@1744: master->allow_scan = 1; fp@1744: master->allow_config = 1; 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@1745: datagram->skip_count++; fp@1744: if (master->debug_level) fp@1744: EC_DBG("skipping datagram %x.\n", (unsigned int) datagram); fp@1715: 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@1715: 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@1732: 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@1732: cycles_t cycles_start, cycles_sent, cycles_end; fp@1732: unsigned long jiffies_sent; fp@293: unsigned int frame_count, more_datagrams_waiting; fp@1732: struct list_head sent_datagrams; fp@1732: fp@1732: cycles_start = get_cycles(); fp@176: frame_count = 0; fp@1732: INIT_LIST_HEAD(&sent_datagrams); fp@208: fp@1715: 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@1744: frame_data = ec_device_tx_data(&master->main_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@1715: 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@1732: list_add_tail(&datagram->sent, &sent_datagrams); fp@293: datagram->index = master->datagram_index++; fp@176: fp@1715: 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@1745: memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN); 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@1732: if (list_empty(&sent_datagrams)) { fp@1715: 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@1715: 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@1744: ec_device_send(&master->main_device, cur_data - frame_data); fp@1732: cycles_sent = get_cycles(); fp@1732: jiffies_sent = jiffies; fp@1732: fp@1732: // set datagram states and sending timestamps fp@1732: list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) { fp@1732: datagram->state = EC_DATAGRAM_SENT; fp@1732: datagram->cycles_sent = cycles_sent; fp@1732: datagram->jiffies_sent = jiffies_sent; fp@1732: list_del_init(&datagram->sent); // empty list of sent datagrams fp@1732: } fp@1732: fp@176: frame_count++; fp@176: } fp@293: while (more_datagrams_waiting); fp@98: fp@1715: if (unlikely(master->debug_level > 1)) { fp@1719: cycles_end = get_cycles(); fp@293: EC_DBG("ec_master_send_datagrams sent %i frames in %ius.\n", fp@1719: frame_count, fp@1719: (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@1716: void ec_master_receive_datagrams(ec_master_t *master, /**< EtherCAT master */ fp@1716: const uint8_t *frame_data, /**< frame data */ fp@1716: size_t size /**< size of the received data */ fp@1716: ) 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@1744: if (datagram->index == datagram_index fp@1744: && datagram->state == EC_DATAGRAM_SENT fp@293: && datagram->type == datagram_type 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@1744: fp@1744: if (unlikely(master->debug_level > 0)) { fp@1744: EC_DBG("UNMATCHED datagram:\n"); fp@1744: ec_print_data(cur_data - EC_DATAGRAM_HEADER_SIZE, fp@1744: EC_DATAGRAM_HEADER_SIZE + data_size fp@1744: + EC_DATAGRAM_FOOTER_SIZE); fp@1744: #ifdef EC_DEBUG_RING fp@1744: ec_device_debug_ring_print(&master->main_device); fp@1744: #endif fp@1744: } fp@1744: fp@293: cur_data += data_size + EC_DATAGRAM_FOOTER_SIZE; fp@98: continue; fp@98: } fp@98: fp@1719: // 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@1715: datagram->state = EC_DATAGRAM_RECEIVED; fp@1744: datagram->cycles_received = master->main_device.cycles_poll; fp@1744: datagram->jiffies_received = master->main_device.jiffies_poll; 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@1719: if (unlikely(jiffies - master->stats.output_jiffies >= HZ)) { fp@1719: master->stats.output_jiffies = jiffies; fp@1719: fp@98: if (master->stats.timeouts) { fp@1732: EC_WARN("%i datagram%s TIMED OUT!\n", master->stats.timeouts, fp@1732: master->stats.timeouts == 1 ? "" : "s"); fp@98: master->stats.timeouts = 0; fp@98: } fp@98: if (master->stats.corrupted) { fp@1732: EC_WARN("%i frame%s CORRUPTED!\n", master->stats.corrupted, fp@1732: master->stats.corrupted == 1 ? "" : "s"); fp@98: master->stats.corrupted = 0; fp@98: } fp@98: if (master->stats.unmatched) { fp@1732: EC_WARN("%i datagram%s UNMATCHED!\n", master->stats.unmatched, fp@1732: 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@1744: * Master kernel thread function for IDLE mode. fp@1744: */ fp@1744: fp@1744: static int ec_master_idle_thread(ec_master_t *master) fp@1744: { fp@1719: cycles_t cycles_start, cycles_end; fp@251: fp@1744: daemonize("EtherCAT-IDLE"); fp@1739: allow_signal(SIGTERM); fp@1739: fp@1744: while (!signal_pending(current)) { fp@1739: cycles_start = get_cycles(); fp@1745: ec_datagram_output_stats(&master->fsm_datagram); fp@1739: fp@1744: if (ec_fsm_master_running(&master->fsm)) { // datagram on the way fp@1744: // receive fp@1744: spin_lock_bh(&master->internal_lock); fp@1744: ecrt_master_receive(master); fp@1744: spin_unlock_bh(&master->internal_lock); fp@1744: fp@1744: if (master->fsm_datagram.state == EC_DATAGRAM_SENT) fp@1744: goto schedule; fp@1744: } fp@1739: fp@1739: // execute master state machine fp@1744: if (ec_fsm_master_exec(&master->fsm)) { // datagram ready for sending fp@1744: // queue and send fp@1744: spin_lock_bh(&master->internal_lock); fp@1744: ec_master_queue_datagram(master, &master->fsm_datagram); fp@1744: ecrt_master_send(master); fp@1744: spin_unlock_bh(&master->internal_lock); fp@1744: } fp@1739: fp@1739: cycles_end = get_cycles(); fp@1739: master->idle_cycle_times[master->idle_cycle_time_pos] fp@1739: = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; fp@1739: master->idle_cycle_time_pos++; fp@1739: master->idle_cycle_time_pos %= HZ; fp@1739: fp@1744: schedule: fp@1744: if (ec_fsm_master_idle(&master->fsm)) { fp@1744: set_current_state(TASK_INTERRUPTIBLE); fp@1744: schedule_timeout(1); fp@1744: } fp@1744: else { fp@1744: schedule(); fp@1744: } fp@1739: } fp@1739: fp@1739: master->thread_id = 0; fp@1744: if (master->debug_level) fp@1744: EC_DBG("Master IDLE thread exiting...\n"); fp@1739: complete_and_exit(&master->thread_exit, 0); fp@195: } fp@195: fp@195: /*****************************************************************************/ fp@195: fp@195: /** fp@1744: * Master kernel thread function for IDLE mode. fp@1744: */ fp@1744: fp@1744: static int ec_master_operation_thread(ec_master_t *master) fp@1744: { fp@1744: cycles_t cycles_start, cycles_end; fp@1744: fp@1744: daemonize("EtherCAT-OP"); fp@1744: allow_signal(SIGTERM); fp@1744: fp@1744: while (!signal_pending(current)) { fp@1745: ec_datagram_output_stats(&master->fsm_datagram); fp@1744: if (master->injection_seq_rt != master->injection_seq_fsm || fp@1744: master->fsm_datagram.state == EC_DATAGRAM_SENT || fp@1744: master->fsm_datagram.state == EC_DATAGRAM_QUEUED) fp@1744: goto schedule; fp@1744: fp@1744: cycles_start = get_cycles(); fp@1744: fp@1744: // output statistics fp@1744: ec_master_output_stats(master); fp@1744: fp@1744: // execute master state machine fp@1744: if (ec_fsm_master_exec(&master->fsm)) { fp@1744: // inject datagram fp@1744: master->injection_seq_fsm++; fp@1744: } fp@1744: fp@1744: cycles_end = get_cycles(); fp@1744: master->idle_cycle_times[master->idle_cycle_time_pos] fp@1744: = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; fp@1744: master->idle_cycle_time_pos++; fp@1744: master->idle_cycle_time_pos %= HZ; fp@1744: fp@1744: schedule: fp@1744: if (ec_fsm_master_idle(&master->fsm)) { fp@1744: set_current_state(TASK_INTERRUPTIBLE); fp@1744: schedule_timeout(1); fp@1744: } fp@1744: else { fp@1744: schedule(); fp@1744: } fp@1744: } fp@1744: fp@1744: master->thread_id = 0; fp@1744: if (master->debug_level) fp@1744: EC_DBG("Master OP thread exiting...\n"); fp@1744: complete_and_exit(&master->thread_exit, 0); fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1746: /** fp@1746: * Prints the device information to a buffer. fp@1746: * \return number of bytes written. fp@1746: */ fp@1746: fp@1744: ssize_t ec_master_device_info( fp@1746: const ec_device_t *device, /**< EtherCAT device */ fp@1746: const uint8_t *mac, /**< MAC address */ fp@1746: char *buffer /**< target buffer */ fp@1744: ) fp@1744: { fp@1744: unsigned int frames_lost; fp@1744: off_t off = 0; fp@1744: fp@1744: if (ec_mac_is_zero(mac)) { fp@1744: off += sprintf(buffer + off, "none.\n"); fp@1744: } fp@1744: else { fp@1744: off += ec_mac_print(mac, buffer + off); fp@1744: fp@1744: if (device->dev) { fp@1744: off += sprintf(buffer + off, " (connected).\n"); fp@1744: off += sprintf(buffer + off, " Frames sent: %u\n", fp@1744: device->tx_count); fp@1744: off += sprintf(buffer + off, " Frames received: %u\n", fp@1744: device->rx_count); fp@1744: frames_lost = device->tx_count - device->rx_count; fp@1744: if (frames_lost) frames_lost--; fp@1744: off += sprintf(buffer + off, " Frames lost: %u\n", frames_lost); fp@1744: } fp@1744: else { fp@1744: off += sprintf(buffer + off, " (WAITING).\n"); fp@1744: } fp@1744: } fp@1744: fp@1744: return off; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1716: Formats master information for SysFS read access. fp@1716: \return number of bytes written fp@1716: */ fp@1716: fp@1716: ssize_t ec_master_info(ec_master_t *master, /**< EtherCAT master */ fp@1716: char *buffer /**< memory to store data */ fp@1716: ) fp@1716: { fp@1716: off_t off = 0; fp@1745: #ifdef EC_EOE fp@1716: ec_eoe_t *eoe; fp@1745: #endif fp@1716: uint32_t cur, sum, min, max, pos, i; fp@1744: fp@1716: off += sprintf(buffer + off, "\nMode: "); fp@1716: switch (master->mode) { fp@1716: case EC_MASTER_MODE_ORPHANED: fp@1716: off += sprintf(buffer + off, "ORPHANED"); fp@1716: break; fp@1716: case EC_MASTER_MODE_IDLE: fp@1716: off += sprintf(buffer + off, "IDLE"); fp@1716: break; fp@1716: case EC_MASTER_MODE_OPERATION: fp@1716: off += sprintf(buffer + off, "OPERATION"); fp@1716: break; fp@1716: } fp@1716: fp@1716: off += sprintf(buffer + off, "\nSlaves: %i\n", fp@1716: master->slave_count); fp@1745: off += sprintf(buffer + off, "Status: %s\n", fp@1745: master->fsm.tainted ? "TAINTED" : "sane"); fp@1745: off += sprintf(buffer + off, "PDO slaves: %s\n", fp@1745: master->pdo_slaves_offline ? "INCOMPLETE" : "online"); fp@1744: fp@1744: off += sprintf(buffer + off, "\nDevices:\n"); fp@1744: fp@1744: down(&master->device_sem); fp@1744: off += sprintf(buffer + off, " Main: "); fp@1744: off += ec_master_device_info(&master->main_device, fp@1744: master->main_mac, buffer + off); fp@1744: off += sprintf(buffer + off, " Backup: "); fp@1744: off += ec_master_device_info(&master->backup_device, fp@1744: master->backup_mac, buffer + off); fp@1744: up(&master->device_sem); fp@1716: fp@1716: off += sprintf(buffer + off, "\nTiming (min/avg/max) [us]:\n"); fp@1716: fp@1716: sum = 0; fp@1716: min = 0xFFFFFFFF; fp@1716: max = 0; fp@1716: pos = master->idle_cycle_time_pos; fp@1716: for (i = 0; i < HZ; i++) { fp@1716: cur = master->idle_cycle_times[(i + pos) % HZ]; fp@1716: sum += cur; fp@1716: if (cur < min) min = cur; fp@1716: if (cur > max) max = cur; fp@1716: } fp@1716: off += sprintf(buffer + off, " Idle cycle: %u / %u.%u / %u\n", fp@1716: min, sum / HZ, (sum * 100 / HZ) % 100, max); fp@1716: fp@1745: #ifdef EC_EOE fp@1716: sum = 0; fp@1716: min = 0xFFFFFFFF; fp@1716: max = 0; fp@1716: pos = master->eoe_cycle_time_pos; fp@1716: for (i = 0; i < HZ; i++) { fp@1716: cur = master->eoe_cycle_times[(i + pos) % HZ]; fp@1716: sum += cur; fp@1716: if (cur < min) min = cur; fp@1716: if (cur > max) max = cur; fp@1716: } fp@1716: off += sprintf(buffer + off, " EoE cycle: %u / %u.%u / %u\n", fp@1716: min, sum / HZ, (sum * 100 / HZ) % 100, max); fp@1716: fp@1716: if (!list_empty(&master->eoe_handlers)) fp@1716: off += sprintf(buffer + off, "\nEoE statistics (RX/TX) [bps]:\n"); fp@1716: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1716: off += sprintf(buffer + off, " %s: %u / %u (%u KB/s)\n", fp@1716: eoe->dev->name, eoe->rx_rate, eoe->tx_rate, fp@1716: ((eoe->rx_rate + eoe->tx_rate) / 8 + 512) / 1024); fp@1716: } fp@1745: #endif fp@1716: fp@1716: off += sprintf(buffer + off, "\n"); fp@1716: fp@1716: return off; fp@1716: } fp@1716: fp@1716: /*****************************************************************************/ fp@1716: fp@1716: /** 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@1716: if (attr == &attr_info) { fp@1716: return ec_master_info(master, buffer); fp@1715: } fp@1715: else if (attr == &attr_debug_level) { fp@1715: return sprintf(buffer, "%i\n", master->debug_level); fp@1715: } 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@1744: if (attr == &attr_debug_level) { fp@1715: if (!strcmp(buffer, "0\n")) { fp@1715: master->debug_level = 0; fp@1715: } fp@1715: else if (!strcmp(buffer, "1\n")) { fp@1715: master->debug_level = 1; fp@1715: } fp@1715: else if (!strcmp(buffer, "2\n")) { fp@1715: master->debug_level = 2; fp@1715: } fp@1715: else { fp@1715: EC_ERR("Invalid debug level value!\n"); fp@1715: return -EINVAL; fp@1715: } fp@1715: fp@1715: EC_INFO("Master debug level set to %i.\n", master->debug_level); fp@1715: return size; fp@1715: } fp@268: fp@268: return -EINVAL; fp@268: } fp@268: fp@268: /*****************************************************************************/ fp@268: fp@1745: #ifdef EC_EOE fp@268: /** fp@1716: 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@1744: if (master->eoe_running) { fp@1744: EC_WARN("EoE already running!\n"); fp@1744: return; fp@1744: } fp@1744: fp@1744: if (list_empty(&master->eoe_handlers)) fp@1744: return; fp@1744: fp@1744: if (!master->request_cb || !master->release_cb) { fp@1744: EC_WARN("No EoE processing because of missing locking callbacks!\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: } 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: 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: master->eoe_running = 0; fp@251: } fp@251: fp@251: /*****************************************************************************/ fp@1732: 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@1744: unsigned int none_open = 1; fp@1719: cycles_t cycles_start, cycles_end; fp@1719: unsigned long restart_jiffies; fp@235: fp@251: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1744: if (ec_eoe_is_open(eoe)) { fp@1744: none_open = 0; fp@1744: break; fp@1744: } fp@1744: } fp@1744: if (none_open) fp@1744: goto queue_timer; fp@1744: fp@1745: // receive datagrams fp@1744: if (master->request_cb(master->cb_data)) goto queue_timer; fp@1719: cycles_start = get_cycles(); fp@1715: ecrt_master_receive(master); fp@1745: master->release_cb(master->cb_data); fp@1739: fp@1739: // actual EoE processing fp@251: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@251: ec_eoe_run(eoe); fp@251: } fp@1716: fp@1739: // send datagrams fp@1745: if (master->request_cb(master->cb_data)) { fp@1745: goto queue_timer; fp@1745: } fp@1745: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1745: ec_eoe_queue(eoe); fp@1745: } fp@1715: ecrt_master_send(master); fp@1745: master->release_cb(master->cb_data); fp@1719: cycles_end = get_cycles(); fp@1744: fp@1716: master->eoe_cycle_times[master->eoe_cycle_time_pos] fp@1719: = (u32) (cycles_end - cycles_start) * 1000 / cpu_khz; fp@1716: master->eoe_cycle_time_pos++; fp@1716: master->eoe_cycle_time_pos %= HZ; fp@1716: fp@251: queue_timer: fp@1719: restart_jiffies = HZ / EC_EOE_FREQUENCY; fp@1719: if (!restart_jiffies) restart_jiffies = 1; fp@1745: master->eoe_timer.expires = jiffies + restart_jiffies; fp@208: add_timer(&master->eoe_timer); fp@197: } fp@1745: #endif fp@1716: fp@1739: /*****************************************************************************/ fp@1739: fp@1739: /** fp@1739: Prepares synchronous IO. fp@1739: Queues all domain datagrams and sends them. Then waits a certain time, so fp@1739: that ecrt_master_receive() can be called securely. fp@1739: */ fp@1739: fp@1739: void ec_master_prepare(ec_master_t *master /**< EtherCAT master */) fp@1739: { fp@1739: ec_domain_t *domain; fp@1739: cycles_t cycles_start, cycles_end, cycles_timeout; fp@1739: fp@1739: // queue datagrams of all domains fp@1739: list_for_each_entry(domain, &master->domains, list) fp@1739: ecrt_domain_queue(domain); fp@1739: fp@1739: ecrt_master_send(master); fp@1739: fp@1739: cycles_start = get_cycles(); fp@1739: cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); fp@1739: fp@1739: // active waiting fp@1739: while (1) { fp@1739: udelay(100); fp@1739: cycles_end = get_cycles(); fp@1739: if (cycles_end - cycles_start >= cycles_timeout) break; fp@1739: } fp@1739: } fp@1739: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Translates an ASCII coded bus-address to a slave pointer. fp@1744: * These are the valid addressing schemes: fp@1744: * - \a "X" = the Xth slave on the bus (ring position), fp@1744: * - \a "#X" = the slave with alias X, fp@1744: * - \a "#X:Y" = the Yth slave after the slave with alias X. fp@1744: * X and Y are zero-based indices and may be provided in hexadecimal or octal fp@1744: * notation (with appropriate prefix). fp@1744: * \return pointer to the slave on success, else NULL fp@1744: */ fp@1744: fp@1744: ec_slave_t *ec_master_parse_slave_address( fp@1744: const ec_master_t *master, /**< EtherCAT master */ fp@1744: const char *address /**< address string */ fp@1744: ) fp@1744: { fp@1744: unsigned long first, second; fp@1744: char *remainder, *remainder2; fp@1744: const char *original; fp@1744: unsigned int alias_requested = 0, alias_not_found = 1; fp@1744: ec_slave_t *alias_slave = NULL, *slave; fp@1744: fp@1744: original = address; fp@1744: fp@1744: if (!address[0]) fp@1744: goto out_invalid; fp@1744: fp@1744: if (address[0] == '#') { fp@1744: alias_requested = 1; fp@1744: address++; fp@1744: } fp@1744: fp@1744: first = simple_strtoul(address, &remainder, 0); fp@1744: if (remainder == address) fp@1744: goto out_invalid; fp@1744: fp@1744: if (alias_requested) { fp@1744: list_for_each_entry(alias_slave, &master->slaves, list) { fp@1744: if (alias_slave->sii_alias == first) { fp@1744: alias_not_found = 0; fp@1744: break; fp@1744: } fp@1744: } fp@1744: if (alias_not_found) { fp@1744: EC_ERR("Alias not found!\n"); fp@1744: goto out_invalid; fp@1744: } fp@1744: } fp@1744: fp@1744: if (!remainder[0]) { fp@1744: if (alias_requested) { // alias addressing fp@1744: return alias_slave; fp@1744: } fp@1744: else { // position addressing fp@1744: list_for_each_entry(slave, &master->slaves, list) { fp@1744: if (slave->ring_position == first) return slave; fp@1744: } fp@1744: EC_ERR("Slave index out of range!\n"); fp@1744: goto out_invalid; fp@1744: } fp@1744: } fp@1744: else if (alias_requested && remainder[0] == ':') { // field addressing fp@1744: struct list_head *list; fp@1744: remainder++; fp@1744: second = simple_strtoul(remainder, &remainder2, 0); fp@1744: fp@1744: if (remainder2 == remainder || remainder2[0]) fp@1744: goto out_invalid; fp@1744: fp@1744: list = &alias_slave->list; fp@1744: while (second--) { fp@1744: list = list->next; fp@1744: if (list == &master->slaves) { // last slave exceeded fp@1744: EC_ERR("Slave index out of range!\n"); fp@1744: goto out_invalid; fp@1744: } fp@1744: } fp@1744: return list_entry(list, ec_slave_t, list); fp@1744: } fp@1744: fp@1744: out_invalid: fp@1744: EC_ERR("Invalid slave address string \"%s\"!\n", original); fp@1744: return NULL; fp@1744: } fp@1744: 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@1732: return NULL; 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@1732: return NULL; fp@178: } fp@178: fp@95: list_add_tail(&domain->list, &master->domains); fp@1732: fp@73: return domain; 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@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@1744: // request slave configuration fp@1744: down(&master->config_sem); fp@1744: master->allow_config = 1; // request the current configuration fp@1744: master->config_state = EC_REQUEST_IN_PROGRESS; fp@1744: up(&master->config_sem); fp@1744: fp@1744: // wait for configuration to complete fp@1744: if (wait_event_interruptible(master->config_queue, fp@1744: master->config_state != EC_REQUEST_IN_PROGRESS)) { fp@1744: EC_INFO("Waiting for configuration interrupted by signal.\n"); fp@1744: return -1; fp@1744: } fp@1744: fp@1744: if (master->config_state != EC_REQUEST_COMPLETE) { fp@1744: EC_ERR("Failed to configure slaves.\n"); fp@1744: return -1; fp@1744: } fp@1744: fp@1744: // restart EoE process and master thread with new locking fp@1745: #ifdef EC_EOE fp@1744: ec_master_eoe_stop(master); fp@1745: #endif fp@1744: ec_master_thread_stop(master); fp@1744: fp@1739: ec_master_prepare(master); // prepare asynchronous IO fp@1739: fp@1744: if (master->debug_level) fp@1744: EC_DBG("FSM datagram is %x.\n", (unsigned int) &master->fsm_datagram); fp@1744: fp@1744: master->injection_seq_fsm = 0; fp@1744: master->injection_seq_rt = 0; fp@1744: master->request_cb = master->ext_request_cb; fp@1744: master->release_cb = master->ext_release_cb; fp@1744: master->cb_data = master->ext_cb_data; fp@1744: fp@1744: if (ec_master_thread_start(master, ec_master_operation_thread)) { fp@1744: EC_ERR("Failed to start master thread!\n"); fp@1744: return -1; fp@1744: } fp@1745: #ifdef EC_EOE fp@1744: ec_master_eoe_start(master); fp@1745: #endif fp@73: return 0; fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@293: Asynchronous sending of datagrams. fp@199: \ingroup RealtimeInterface fp@195: */ fp@195: fp@1715: void ecrt_master_send(ec_master_t *master /**< EtherCAT master */) fp@104: { fp@293: ec_datagram_t *datagram, *n; fp@104: fp@1744: if (master->injection_seq_rt != master->injection_seq_fsm) { fp@1744: // inject datagram produced by master FSM fp@1744: ec_master_queue_datagram(master, &master->fsm_datagram); fp@1744: master->injection_seq_rt = master->injection_seq_fsm; fp@1744: } fp@1744: fp@1744: if (unlikely(!master->main_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@1715: datagram->state = EC_DATAGRAM_ERROR; fp@293: list_del_init(&datagram->queue); fp@104: } fp@104: fp@195: // query link state fp@1744: ec_device_poll(&master->main_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@1715: void ecrt_master_receive(ec_master_t *master /**< EtherCAT master */) fp@104: { fp@293: ec_datagram_t *datagram, *next; fp@1732: cycles_t cycles_timeout; fp@1744: unsigned int frames_timed_out = 0; fp@1732: fp@1732: // receive datagrams fp@1744: ec_device_poll(&master->main_device); fp@104: fp@1732: cycles_timeout = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000); fp@293: fp@293: // dequeue all datagrams that timed out fp@293: list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) { fp@1739: if (datagram->state != EC_DATAGRAM_SENT) continue; fp@1739: fp@1744: if (master->main_device.cycles_poll - datagram->cycles_sent fp@1739: > cycles_timeout) { fp@1744: frames_timed_out = 1; fp@1739: list_del_init(&datagram->queue); fp@1739: datagram->state = EC_DATAGRAM_TIMED_OUT; fp@1739: master->stats.timeouts++; fp@1739: ec_master_output_stats(master); fp@1744: fp@1744: if (unlikely(master->debug_level > 0)) { fp@1744: EC_DBG("TIMED OUT datagram %08x, index %02X waited %u us.\n", fp@1744: (unsigned int) datagram, datagram->index, fp@1744: (unsigned int) (master->main_device.cycles_poll fp@1744: - datagram->cycles_sent) * 1000 / cpu_khz); fp@1744: } fp@1744: } fp@1744: } fp@1744: fp@1744: master->frames_timed_out = frames_timed_out; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Obtains a slave pointer by its bus address. fp@1744: * A valid slave pointer is only returned, if vendor ID and product code are fp@1744: * matching. fp@1744: * \return pointer to the slave on success, else NULL fp@1744: * \ingroup RealtimeInterface fp@1744: */ fp@1744: fp@1744: ec_slave_t *ecrt_master_get_slave( fp@1744: const ec_master_t *master, /**< EtherCAT master */ fp@1744: const char *address, /**< address string fp@1744: \see ec_master_parse_slave_address() */ fp@1744: uint32_t vendor_id, /**< vendor ID */ fp@1744: uint32_t product_code /**< product code */ fp@1744: ) fp@1744: { fp@1744: ec_slave_t *slave = ec_master_parse_slave_address(master, address); fp@1744: fp@1744: if (!slave) fp@197: return NULL; fp@1744: fp@1744: return ec_slave_validate(slave, vendor_id, product_code) ? NULL : slave; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Obtains a slave pointer by its ring position. fp@1744: * A valid slave pointer is only returned, if vendor ID and product code are fp@1744: * matching. fp@1744: * \return pointer to the slave on success, else NULL fp@1744: * \ingroup RealtimeInterface fp@1744: */ fp@1744: fp@1744: ec_slave_t *ecrt_master_get_slave_by_pos( fp@1744: const ec_master_t *master, /**< EtherCAT master */ fp@1744: uint16_t ring_position, /**< ring position */ fp@1744: uint32_t vendor_id, /**< vendor ID */ fp@1744: uint32_t product_code /**< product code */ fp@1744: ) fp@1744: { fp@1744: ec_slave_t *slave; fp@1744: unsigned int found = 0; fp@1744: fp@1744: list_for_each_entry(slave, &master->slaves, list) { fp@1744: if (slave->ring_position == ring_position) { fp@1744: found = 1; fp@1744: break; fp@1744: } fp@1744: } fp@1744: fp@1744: if (!found) { fp@1744: EC_ERR("Slave index out of range!\n"); fp@1744: return NULL; fp@1744: } fp@1744: fp@1744: return ec_slave_validate(slave, vendor_id, product_code) ? NULL : slave; 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@1744: master->ext_request_cb = request_cb; fp@1744: master->ext_release_cb = release_cb; fp@1744: master->ext_cb_data = cb_data; fp@1744: } fp@1744: fp@1744: /*****************************************************************************/ fp@1744: fp@1744: /** fp@1744: * Reads the current master status. fp@1744: */ fp@1744: fp@1744: void ecrt_master_get_status(const ec_master_t *master, /**< EtherCAT master */ fp@1744: ec_master_status_t *status /**< target status object */ fp@1744: ) fp@1744: { fp@1744: status->bus_status = fp@1744: (master->pdo_slaves_offline || master->frames_timed_out) fp@1744: ? EC_BUS_FAILURE : EC_BUS_OK; fp@1744: status->bus_tainted = master->fsm.tainted; fp@1744: status->slaves_responding = master->fsm.slaves_responding; 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@1715: EXPORT_SYMBOL(ecrt_master_send); fp@1715: EXPORT_SYMBOL(ecrt_master_receive); fp@206: EXPORT_SYMBOL(ecrt_master_callbacks); fp@138: EXPORT_SYMBOL(ecrt_master_get_slave); fp@1744: EXPORT_SYMBOL(ecrt_master_get_slave_by_pos); fp@1744: EXPORT_SYMBOL(ecrt_master_get_status); fp@42: fp@199: /** \endcond */ fp@199: fp@199: /*****************************************************************************/