fp@39: /****************************************************************************** fp@0: * fp@39: * $Id$ fp@0: * fp@1618: * Copyright (C) 2006 Florian Pose, Ingenieurgemeinschaft IgH fp@1618: * fp@1618: * This file is part of the IgH EtherCAT Master. fp@1618: * fp@1618: * The IgH EtherCAT Master is free software; you can redistribute it fp@1618: * and/or modify it under the terms of the GNU General Public License fp@1619: * as published by the Free Software Foundation; either version 2 of the fp@1619: * License, or (at your option) any later version. fp@1618: * fp@1618: * The IgH EtherCAT Master is distributed in the hope that it will be fp@1618: * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1618: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the fp@1618: * GNU General Public License for more details. fp@1618: * fp@1618: * You should have received a copy of the GNU General Public License fp@1618: * along with the IgH EtherCAT Master; if not, write to the Free Software fp@1618: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@1618: * fp@1619: * The right to use EtherCAT Technology is granted and comes free of fp@1619: * charge under condition of compatibility of product made by fp@1619: * Licensee. People intending to distribute/sell products based on the fp@1619: * code, have to sign an agreement to guarantee that products using fp@1619: * software based on IgH EtherCAT master stay compatible with the actual fp@1619: * EtherCAT specification (which are released themselves as an open fp@1619: * standard) as the (only) precondition to have the right to use EtherCAT fp@1619: * Technology, IP and trade marks. fp@1619: * fp@39: *****************************************************************************/ fp@0: fp@1618: /** fp@1618: \file fp@1618: EtherCAT master methods. fp@1618: */ fp@1618: fp@1618: /*****************************************************************************/ fp@1618: 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@55: #include "types.h" fp@54: #include "device.h" fp@98: #include "command.h" fp@145: #include "ethernet.h" fp@0: fp@39: /*****************************************************************************/ fp@0: fp@1619: void ec_master_freerun(void *); fp@1619: void ec_master_eoe_run(unsigned long); fp@179: ssize_t ec_show_master_attribute(struct kobject *, struct attribute *, char *); fp@1621: ssize_t ec_store_master_attribute(struct kobject *, struct attribute *, fp@1621: const char *, size_t); fp@179: fp@179: /*****************************************************************************/ fp@179: fp@1618: /** \cond */ fp@1618: fp@184: EC_SYSFS_READ_ATTR(slave_count); fp@191: EC_SYSFS_READ_ATTR(mode); fp@1621: EC_SYSFS_READ_WRITE_ATTR(eeprom_write_enable); fp@178: fp@178: static struct attribute *ec_def_attrs[] = { fp@178: &attr_slave_count, fp@191: &attr_mode, fp@1621: &attr_eeprom_write_enable, fp@178: NULL, fp@178: }; fp@178: fp@178: static struct sysfs_ops ec_sysfs_ops = { fp@178: .show = &ec_show_master_attribute, fp@1621: .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@1618: /** \endcond */ fp@1618: 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@1619: unsigned int index, /**< master index */ fp@1619: unsigned int eoe_devices /**< number of EoE devices */ fp@178: ) fp@178: { fp@1619: ec_eoe_t *eoe, *next_eoe; fp@1619: unsigned int i; fp@1619: fp@178: EC_INFO("Initializing master %i.\n", index); fp@178: fp@178: master->index = index; fp@98: master->device = NULL; fp@178: master->reserved = 0; fp@182: INIT_LIST_HEAD(&master->slaves); fp@144: INIT_LIST_HEAD(&master->command_queue); fp@95: INIT_LIST_HEAD(&master->domains); fp@1619: INIT_LIST_HEAD(&master->eoe_handlers); fp@1619: ec_command_init(&master->simple_command); fp@1619: INIT_WORK(&master->freerun_work, ec_master_freerun, (void *) master); fp@1619: init_timer(&master->eoe_timer); fp@1619: master->eoe_timer.function = ec_master_eoe_run; fp@1619: master->eoe_timer.data = (unsigned long) master; fp@1619: master->internal_lock = SPIN_LOCK_UNLOCKED; fp@1619: master->eoe_running = 0; fp@1619: fp@1619: // create workqueue fp@1619: if (!(master->workqueue = create_singlethread_workqueue("EtherCAT"))) { fp@1619: EC_ERR("Failed to create master workqueue.\n"); fp@1619: goto out_return; fp@1619: } fp@1619: fp@1619: // create EoE handlers fp@1619: for (i = 0; i < eoe_devices; i++) { fp@1619: if (!(eoe = (ec_eoe_t *) kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) { fp@1619: EC_ERR("Failed to allocate EoE-Object.\n"); fp@1619: goto out_clear_eoe; fp@1619: } fp@1619: if (ec_eoe_init(eoe)) { fp@1619: kfree(eoe); fp@1619: goto out_clear_eoe; fp@1619: } fp@1619: list_add_tail(&eoe->list, &master->eoe_handlers); fp@1619: } fp@1619: fp@1619: // create state machine object fp@1619: if (ec_fsm_init(&master->fsm, master)) goto out_clear_eoe; fp@98: 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@178: kobject_put(&master->kobj); fp@178: return -1; fp@178: } fp@178: fp@98: ec_master_reset(master); fp@178: return 0; fp@1619: fp@1619: out_clear_eoe: fp@1619: list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { fp@1619: list_del(&eoe->list); fp@1619: ec_eoe_clear(eoe); fp@1619: kfree(eoe); fp@1619: } fp@1619: destroy_workqueue(master->workqueue); fp@1619: out_return: fp@1619: return -1; fp@0: } fp@0: fp@39: /*****************************************************************************/ fp@0: fp@0: /** fp@195: Master destructor. fp@195: Removes all pending commands, 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@1619: ec_eoe_t *eoe, *next_eoe; fp@178: fp@178: EC_INFO("Clearing master %i...\n", master->index); fp@178: fp@73: ec_master_reset(master); fp@1619: ec_fsm_clear(&master->fsm); fp@1619: ec_command_clear(&master->simple_command); fp@1619: destroy_workqueue(master->workqueue); fp@1619: fp@1619: // clear EoE objects fp@1619: list_for_each_entry_safe(eoe, next_eoe, &master->eoe_handlers, list) { fp@1619: list_del(&eoe->list); fp@1619: ec_eoe_clear(eoe); fp@1619: kfree(eoe); fp@1619: } fp@98: fp@98: if (master->device) { fp@98: ec_device_clear(master->device); fp@98: kfree(master->device); fp@98: } fp@144: fp@178: EC_INFO("Master %i cleared.\n", master->index); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Resets the master. fp@195: Note: This function has to be called, everytime ec_master_release() is fp@195: called, to free the slave list, domains etc. fp@195: */ fp@195: fp@195: void ec_master_reset(ec_master_t *master /**< EtherCAT master */) fp@56: { fp@144: ec_command_t *command, *next_c; fp@144: ec_domain_t *domain, *next_d; fp@1619: fp@1619: ec_master_eoe_stop(master); fp@191: ec_master_freerun_stop(master); fp@1619: ec_master_clear_slaves(master); fp@98: fp@195: // empty command queue fp@144: list_for_each_entry_safe(command, next_c, &master->command_queue, queue) { fp@182: list_del_init(&command->queue); fp@98: command->state = EC_CMD_ERROR; fp@98: } fp@98: fp@195: // clear domains fp@144: list_for_each_entry_safe(domain, next_d, &master->domains, list) { fp@98: list_del(&domain->list); fp@178: kobject_del(&domain->kobj); fp@178: kobject_put(&domain->kobj); fp@98: } fp@98: fp@98: master->command_index = 0; fp@98: master->debug_level = 0; fp@155: master->timeout = 500; // 500us fp@130: fp@98: master->stats.timeouts = 0; fp@98: master->stats.delayed = 0; fp@98: master->stats.corrupted = 0; fp@98: master->stats.unmatched = 0; fp@98: master->stats.t_last = 0; fp@191: fp@191: master->mode = EC_MASTER_MODE_IDLE; fp@1619: fp@1619: master->request_cb = NULL; fp@1619: master->release_cb = NULL; fp@1619: master->cb_data = NULL; fp@1619: fp@1621: master->eeprom_write_enable = 0; fp@1621: fp@1619: ec_fsm_reset(&master->fsm); fp@1619: } fp@1619: fp@1619: /*****************************************************************************/ fp@1619: fp@1619: /** fp@1619: Clears all slaves. fp@1619: */ fp@1619: fp@1619: void ec_master_clear_slaves(ec_master_t *master) fp@1619: { fp@1619: ec_slave_t *slave, *next_slave; fp@1619: fp@1619: list_for_each_entry_safe(slave, next_slave, &master->slaves, list) { fp@1619: list_del(&slave->list); fp@1619: kobject_del(&slave->kobj); fp@1619: kobject_put(&slave->kobj); fp@1619: } fp@1619: master->slave_count = 0; fp@74: } fp@74: fp@74: /*****************************************************************************/ fp@74: fp@74: /** fp@195: Places a command in the command queue. fp@195: */ fp@195: fp@195: void ec_master_queue_command(ec_master_t *master, /**< EtherCAT master */ fp@195: ec_command_t *command /**< command */ fp@98: ) fp@98: { fp@98: ec_command_t *queued_command; fp@98: fp@195: // check, if the command is already queued fp@144: list_for_each_entry(queued_command, &master->command_queue, queue) { fp@98: if (queued_command == command) { fp@98: command->state = EC_CMD_QUEUED; fp@98: if (unlikely(master->debug_level)) fp@98: EC_WARN("command already queued.\n"); fp@98: return; fp@98: } fp@98: } fp@98: fp@144: list_add_tail(&command->queue, &master->command_queue); fp@98: command->state = EC_CMD_QUEUED; fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@195: Sends the commands in the queue. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: void ec_master_send_commands(ec_master_t *master /**< EtherCAT master */) fp@98: { fp@98: ec_command_t *command; fp@98: size_t command_size; fp@98: uint8_t *frame_data, *cur_data; fp@98: void *follows_word; fp@1619: cycles_t t_start, t_end; fp@176: unsigned int frame_count, more_commands_waiting; fp@176: fp@176: frame_count = 0; fp@1619: t_start = get_cycles(); fp@1619: fp@1619: if (unlikely(master->debug_level > 0)) fp@176: EC_DBG("ec_master_send_commands\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@176: more_commands_waiting = 0; fp@176: fp@195: // fill current frame with commands fp@176: list_for_each_entry(command, &master->command_queue, queue) { fp@176: if (command->state != EC_CMD_QUEUED) continue; fp@176: fp@195: // does the current command fit in the frame? fp@176: command_size = EC_COMMAND_HEADER_SIZE + command->data_size fp@176: + EC_COMMAND_FOOTER_SIZE; fp@1619: if (cur_data - frame_data + command_size > ETH_DATA_LEN) { fp@176: more_commands_waiting = 1; fp@176: break; fp@176: } fp@176: fp@176: command->state = EC_CMD_SENT; fp@1619: command->t_sent = t_start; fp@176: command->index = master->command_index++; fp@176: fp@176: if (unlikely(master->debug_level > 0)) fp@176: EC_DBG("adding command 0x%02X\n", command->index); fp@176: fp@195: // set "command 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@176: // EtherCAT command header fp@176: EC_WRITE_U8 (cur_data, command->type); fp@176: EC_WRITE_U8 (cur_data + 1, command->index); fp@176: EC_WRITE_U32(cur_data + 2, command->address.logical); fp@176: EC_WRITE_U16(cur_data + 6, command->data_size & 0x7FF); fp@176: EC_WRITE_U16(cur_data + 8, 0x0000); fp@176: follows_word = cur_data + 6; fp@176: cur_data += EC_COMMAND_HEADER_SIZE; fp@176: fp@176: // EtherCAT command data fp@176: memcpy(cur_data, command->data, command->data_size); fp@176: cur_data += command->data_size; fp@176: fp@176: // EtherCAT command footer fp@195: EC_WRITE_U16(cur_data, 0x0000); // reset working counter fp@176: cur_data += EC_COMMAND_FOOTER_SIZE; fp@176: } fp@176: fp@176: if (cur_data - frame_data == EC_FRAME_HEADER_SIZE) { fp@176: if (unlikely(master->debug_level > 0)) 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@1619: while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN) fp@176: EC_WRITE_U8(cur_data++, 0x00); fp@98: fp@98: if (unlikely(master->debug_level > 0)) fp@176: EC_DBG("frame size: %i\n", cur_data - frame_data); fp@176: fp@195: // send frame fp@176: ec_device_send(master->device, cur_data - frame_data); fp@176: frame_count++; fp@176: } fp@176: while (more_commands_waiting); fp@98: fp@98: if (unlikely(master->debug_level > 0)) { fp@1619: t_end = get_cycles(); fp@176: EC_DBG("ec_master_send_commands sent %i frames in %ius.\n", fp@1619: frame_count, (u32) (t_end - t_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@195: void ec_master_receive(ec_master_t *master, /**< EtherCAT master */ fp@195: const uint8_t *frame_data, /**< received data */ fp@195: size_t size /**< size of the received data */ fp@195: ) fp@98: { fp@98: size_t frame_size, data_size; fp@98: uint8_t command_type, command_index; fp@98: unsigned int cmd_follows, matched; fp@98: const uint8_t *cur_data; fp@98: ec_command_t *command; 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@195: // process command header fp@98: command_type = EC_READ_U8 (cur_data); fp@98: command_index = EC_READ_U8 (cur_data + 1); fp@98: data_size = EC_READ_U16(cur_data + 6) & 0x07FF; fp@98: cmd_follows = EC_READ_U16(cur_data + 6) & 0x8000; fp@98: cur_data += EC_COMMAND_HEADER_SIZE; fp@98: fp@98: if (unlikely(cur_data - frame_data fp@98: + data_size + EC_COMMAND_FOOTER_SIZE > size)) { fp@98: master->stats.corrupted++; fp@98: ec_master_output_stats(master); fp@98: return; fp@98: } fp@98: fp@195: // search for matching command in the queue fp@98: matched = 0; fp@144: list_for_each_entry(command, &master->command_queue, queue) { fp@98: if (command->state == EC_CMD_SENT fp@98: && command->type == command_type fp@98: && command->index == command_index fp@98: && command->data_size == data_size) { fp@98: matched = 1; fp@98: break; fp@98: } fp@98: } fp@98: fp@195: // no matching command was found fp@98: if (!matched) { fp@98: master->stats.unmatched++; fp@98: ec_master_output_stats(master); fp@98: cur_data += data_size + EC_COMMAND_FOOTER_SIZE; fp@98: continue; fp@98: } fp@98: fp@195: // copy received data in the command memory fp@98: memcpy(command->data, cur_data, data_size); fp@98: cur_data += data_size; fp@98: fp@195: // set the command's working counter fp@98: command->working_counter = EC_READ_U16(cur_data); fp@98: cur_data += EC_COMMAND_FOOTER_SIZE; fp@98: fp@195: // dequeue the received command fp@98: command->state = EC_CMD_RECEIVED; fp@144: list_del_init(&command->queue); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@195: Sends a single command and waits for its reception. fp@195: If the slave doesn't respond, the command is sent again. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_master_simple_io(ec_master_t *master, /**< EtherCAT master */ fp@195: ec_command_t *command /**< command */ fp@145: ) fp@98: { fp@98: unsigned int response_tries_left; fp@98: fp@1621: response_tries_left = 10000; fp@113: fp@113: while (1) fp@98: { fp@98: ec_master_queue_command(master, command); fp@104: ecrt_master_sync_io(master); fp@98: fp@98: if (command->state == EC_CMD_RECEIVED) { fp@113: if (likely(command->working_counter)) fp@113: return 0; fp@98: } fp@98: else if (command->state == EC_CMD_TIMEOUT) { fp@104: EC_ERR("Simple-IO TIMEOUT!\n"); fp@98: return -1; fp@98: } fp@98: else if (command->state == EC_CMD_ERROR) { fp@104: EC_ERR("Simple-IO command error!\n"); fp@98: return -1; fp@98: } fp@104: fp@195: // no direct response, wait a little bit... fp@130: udelay(100); fp@113: fp@113: if (unlikely(--response_tries_left)) { fp@113: EC_ERR("No response in simple-IO!\n"); fp@113: return -1; fp@113: } fp@113: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@195: Scans the EtherCAT bus for slaves. fp@195: Creates a list of slave structures for further processing. fp@195: \return 0 in case of success, else < 0 fp@195: */ fp@195: fp@195: int ec_master_bus_scan(ec_master_t *master /**< EtherCAT master */) fp@98: { fp@1619: ec_slave_t *slave; fp@73: ec_slave_ident_t *ident; fp@1619: ec_command_t *command; fp@73: unsigned int i; fp@188: uint16_t coupler_index, coupler_subindex; fp@188: uint16_t reverse_coupler_index, current_coupler_index; fp@182: fp@182: if (!list_empty(&master->slaves)) { fp@90: EC_ERR("Slave scan already done!\n"); fp@90: return -1; fp@90: } fp@73: fp@144: command = &master->simple_command; fp@144: fp@195: // determine number of slaves on bus fp@144: if (ec_command_brd(command, 0x0000, 4)) return -1; fp@145: if (unlikely(ec_master_simple_io(master, command))) return -1; fp@144: master->slave_count = command->working_counter; fp@84: EC_INFO("Found %i slaves on bus.\n", master->slave_count); fp@73: fp@73: if (!master->slave_count) return 0; fp@73: fp@195: // init slaves fp@73: for (i = 0; i < master->slave_count; i++) { fp@1619: if (!(slave = fp@1619: (ec_slave_t *) kmalloc(sizeof(ec_slave_t), GFP_KERNEL))) { fp@182: EC_ERR("Failed to allocate slave %i!\n", i); fp@182: goto out_free; fp@182: } fp@182: fp@182: if (ec_slave_init(slave, master, i, i + 1)) goto out_free; fp@182: fp@182: if (kobject_add(&slave->kobj)) { fp@182: EC_ERR("Failed to add kobject.\n"); fp@182: kobject_put(&slave->kobj); // free fp@182: goto out_free; fp@182: } fp@182: fp@182: list_add_tail(&slave->list, &master->slaves); fp@182: } fp@182: fp@188: coupler_index = 0; fp@188: reverse_coupler_index = 0xFFFF; fp@188: current_coupler_index = 0x3FFF; fp@188: coupler_subindex = 0; fp@73: fp@195: // for every slave on the bus fp@182: list_for_each_entry(slave, &master->slaves, list) { fp@73: fp@195: // write station address fp@182: if (ec_command_apwr(command, slave->ring_position, 0x0010, fp@182: sizeof(uint16_t))) goto out_free; fp@144: EC_WRITE_U16(command->data, slave->station_address); fp@145: if (unlikely(ec_master_simple_io(master, command))) { fp@182: EC_ERR("Writing station address failed on slave %i!\n", fp@182: slave->ring_position); fp@182: goto out_free; fp@73: } fp@73: fp@195: // fetch all slave information fp@182: if (ec_slave_fetch(slave)) goto out_free; fp@73: fp@195: // search for identification in "database" fp@73: ident = slave_idents; fp@129: while (ident->type) { fp@73: if (unlikely(ident->vendor_id == slave->sii_vendor_id fp@73: && ident->product_code == slave->sii_product_code)) { fp@73: slave->type = ident->type; fp@73: break; fp@73: } fp@73: ident++; fp@73: } fp@73: fp@188: if (!slave->type) { fp@84: EC_WARN("Unknown slave device (vendor 0x%08X, code 0x%08X) at" fp@84: " position %i.\n", slave->sii_vendor_id, fp@1619: slave->sii_product_code, slave->ring_position); fp@188: } fp@188: else if (slave->type->special == EC_TYPE_BUS_COUPLER) { fp@188: if (slave->sii_alias) fp@188: current_coupler_index = reverse_coupler_index--; fp@188: else fp@188: current_coupler_index = coupler_index++; fp@188: coupler_subindex = 0; fp@188: } fp@188: fp@188: slave->coupler_index = current_coupler_index; fp@188: slave->coupler_subindex = coupler_subindex; fp@188: coupler_subindex++; fp@73: } fp@73: fp@73: return 0; fp@182: fp@182: out_free: fp@1619: ec_master_clear_slaves(master); fp@182: return -1; fp@54: } fp@54: fp@54: /*****************************************************************************/ fp@54: fp@54: /** fp@195: Output statistics in cyclic mode. fp@195: This function outputs statistical data on demand, but not more often than fp@195: necessary. The output happens at most once a second. fp@195: */ fp@195: fp@195: void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */) fp@98: { fp@98: cycles_t t_now = get_cycles(); fp@98: fp@98: if (unlikely((u32) (t_now - master->stats.t_last) / cpu_khz > 1000)) { fp@98: if (master->stats.timeouts) { fp@98: EC_WARN("%i commands TIMED OUT!\n", master->stats.timeouts); fp@98: master->stats.timeouts = 0; fp@98: } fp@98: if (master->stats.delayed) { fp@98: EC_WARN("%i frame(s) DELAYED!\n", master->stats.delayed); fp@98: master->stats.delayed = 0; fp@98: } fp@98: if (master->stats.corrupted) { fp@98: EC_WARN("%i frame(s) CORRUPTED!\n", master->stats.corrupted); fp@98: master->stats.corrupted = 0; fp@98: } fp@98: if (master->stats.unmatched) { fp@98: EC_WARN("%i command(s) UNMATCHED!\n", master->stats.unmatched); fp@98: master->stats.unmatched = 0; fp@98: } fp@98: master->stats.t_last = t_now; fp@73: } fp@54: } fp@54: fp@68: /*****************************************************************************/ fp@68: fp@68: /** fp@195: Starts the Free-Run mode. fp@191: */ fp@191: fp@191: void ec_master_freerun_start(ec_master_t *master /**< EtherCAT master */) fp@191: { fp@191: if (master->mode == EC_MASTER_MODE_FREERUN) return; fp@191: fp@191: if (master->mode == EC_MASTER_MODE_RUNNING) { fp@191: EC_ERR("ec_master_freerun_start: Master already running!\n"); fp@191: return; fp@191: } fp@191: fp@191: EC_INFO("Starting Free-Run mode.\n"); fp@191: fp@191: master->mode = EC_MASTER_MODE_FREERUN; fp@1619: ec_fsm_reset(&master->fsm); fp@1619: queue_delayed_work(master->workqueue, &master->freerun_work, 1); fp@191: } fp@191: fp@191: /*****************************************************************************/ fp@191: fp@191: /** fp@195: Stops the Free-Run mode. fp@191: */ fp@191: fp@191: void ec_master_freerun_stop(ec_master_t *master /**< EtherCAT master */) fp@191: { fp@191: if (master->mode != EC_MASTER_MODE_FREERUN) return; fp@191: fp@1619: ec_master_eoe_stop(master); fp@1619: fp@191: EC_INFO("Stopping Free-Run mode.\n"); fp@1621: master->mode = EC_MASTER_MODE_IDLE; fp@191: fp@1619: if (!cancel_delayed_work(&master->freerun_work)) { fp@1619: flush_workqueue(master->workqueue); fp@1619: } fp@1619: fp@1619: ec_master_clear_slaves(master); fp@191: } fp@191: fp@191: /*****************************************************************************/ fp@191: fp@191: /** fp@191: Free-Run mode function. fp@191: */ fp@191: fp@1619: void ec_master_freerun(void *data /**< master pointer */) fp@191: { fp@191: ec_master_t *master = (ec_master_t *) data; fp@191: fp@1619: // aquire master lock fp@1619: spin_lock_bh(&master->internal_lock); fp@1619: fp@195: ecrt_master_async_receive(master); fp@195: fp@1619: // execute master state machine fp@1619: ec_fsm_execute(&master->fsm); fp@195: fp@195: ecrt_master_async_send(master); fp@191: fp@1619: // release master lock fp@1619: spin_unlock_bh(&master->internal_lock); fp@1619: fp@1621: if (master->mode == EC_MASTER_MODE_FREERUN) fp@1621: queue_delayed_work(master->workqueue, &master->freerun_work, 1); fp@191: } fp@191: fp@191: /*****************************************************************************/ fp@191: fp@191: /** fp@195: Initializes a sync manager configuration page. fp@195: The referenced memory (\a data) must be at least EC_SYNC_SIZE bytes. fp@195: */ fp@195: fp@195: void ec_sync_config(const ec_sync_t *sync, /**< sync manager */ fp@1621: const ec_slave_t *slave, /**< EtherCAT slave */ fp@195: uint8_t *data /**> configuration memory */ fp@73: ) fp@73: { fp@1621: size_t sync_size; fp@1621: fp@1621: sync_size = ec_slave_calc_sync_size(slave, sync); fp@1621: fp@77: EC_WRITE_U16(data, sync->physical_start_address); fp@1621: EC_WRITE_U16(data + 2, sync_size); fp@77: EC_WRITE_U8 (data + 4, sync->control_byte); fp@77: EC_WRITE_U8 (data + 5, 0x00); // status byte (read only) fp@77: EC_WRITE_U16(data + 6, 0x0001); // enable fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: 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@195: void ec_eeprom_sync_config(const ec_eeprom_sync_t *sync, /**< sync manager */ fp@195: uint8_t *data /**> configuration memory */ fp@136: ) fp@136: { fp@136: EC_WRITE_U16(data, sync->physical_start_address); fp@136: EC_WRITE_U16(data + 2, sync->length); 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@1621: const ec_slave_t *slave, /**< EtherCAT slave */ fp@195: uint8_t *data /**> configuration memory */ fp@73: ) fp@73: { fp@1621: size_t sync_size; fp@1621: fp@1621: sync_size = ec_slave_calc_sync_size(slave, fmmu->sync); fp@1621: fp@77: EC_WRITE_U32(data, fmmu->logical_start_address); fp@1621: 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@77: EC_WRITE_U8 (data + 11, (fmmu->sync->control_byte & 0x04) ? 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@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@178: if (attr == &attr_slave_count) { fp@178: return sprintf(buffer, "%i\n", master->slave_count); fp@178: } fp@191: else if (attr == &attr_mode) { fp@191: switch (master->mode) { fp@191: case EC_MASTER_MODE_IDLE: fp@191: return sprintf(buffer, "IDLE\n"); fp@191: case EC_MASTER_MODE_FREERUN: fp@191: return sprintf(buffer, "FREERUN\n"); fp@191: case EC_MASTER_MODE_RUNNING: fp@191: return sprintf(buffer, "RUNNING\n"); fp@191: } fp@191: } fp@178: fp@178: return 0; fp@178: } fp@178: fp@178: /*****************************************************************************/ fp@178: fp@178: /** fp@1621: Formats attribute data for SysFS write access. fp@1621: \return number of bytes processed, or negative error code fp@1621: */ fp@1621: fp@1621: ssize_t ec_store_master_attribute(struct kobject *kobj, /**< slave's kobject */ fp@1621: struct attribute *attr, /**< attribute */ fp@1621: const char *buffer, /**< memory with data */ fp@1621: size_t size /**< size of data to store */ fp@1621: ) fp@1621: { fp@1621: ec_master_t *master = container_of(kobj, ec_master_t, kobj); fp@1621: fp@1621: if (attr == &attr_eeprom_write_enable) { fp@1621: if (!strcmp(buffer, "1\n")) { fp@1621: master->eeprom_write_enable = 1; fp@1621: EC_INFO("Slave EEPROM writing enabled.\n"); fp@1621: return size; fp@1621: } fp@1621: else if (!strcmp(buffer, "0\n")) { fp@1621: master->eeprom_write_enable = 0; fp@1621: EC_INFO("Slave EEPROM writing disabled.\n"); fp@1621: return size; fp@1621: } fp@1621: fp@1621: EC_ERR("Invalid value for eeprom_write_enable!\n"); fp@1621: fp@1621: if (master->eeprom_write_enable) { fp@1621: master->eeprom_write_enable = 0; fp@1621: EC_INFO("Slave EEPROM writing disabled.\n"); fp@1621: } fp@1621: } fp@1621: fp@1621: return -EINVAL; fp@1621: } fp@1621: fp@1621: /*****************************************************************************/ fp@1621: fp@1621: /** fp@1619: Starts Ethernet-over-EtherCAT processing for all EoE-capable slaves. fp@1619: */ fp@1619: fp@1619: void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */) fp@1619: { fp@1619: ec_eoe_t *eoe; fp@1619: ec_slave_t *slave; fp@1619: unsigned int coupled, found; fp@1619: fp@1619: if (master->eoe_running) return; fp@1619: fp@1619: // decouple all EoE handlers fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) fp@1619: eoe->slave = NULL; fp@1619: coupled = 0; fp@1619: fp@1619: // couple a free EoE handler to every EoE-capable slave fp@1619: list_for_each_entry(slave, &master->slaves, list) { fp@1619: if (!(slave->sii_mailbox_protocols & EC_MBOX_EOE)) continue; fp@1619: fp@1619: found = 0; fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1619: if (eoe->slave) continue; fp@1619: eoe->slave = slave; fp@1619: found = 1; fp@1619: coupled++; fp@1619: EC_INFO("Coupling device %s to slave %i.\n", fp@1619: eoe->dev->name, slave->ring_position); fp@1619: if (eoe->opened) { fp@1619: slave->requested_state = EC_SLAVE_STATE_OP; fp@1619: } fp@1619: else { fp@1619: slave->requested_state = EC_SLAVE_STATE_INIT; fp@1619: } fp@1619: slave->state_error = 0; fp@1619: break; fp@1619: } fp@1619: fp@1619: if (!found) { fp@1619: EC_WARN("No EoE handler for slave %i!\n", slave->ring_position); fp@1619: slave->requested_state = EC_SLAVE_STATE_INIT; fp@1619: slave->state_error = 0; fp@1619: } fp@1619: } fp@1619: fp@1619: if (!coupled) { fp@1619: EC_INFO("No EoE handlers coupled.\n"); fp@1619: return; fp@1619: } fp@1619: fp@1619: EC_INFO("Starting EoE processing.\n"); fp@1619: master->eoe_running = 1; fp@1619: fp@1619: // start EoE processing fp@1619: master->eoe_timer.expires = jiffies + 10; fp@1619: add_timer(&master->eoe_timer); fp@1619: return; fp@1619: } fp@1619: fp@1619: /*****************************************************************************/ fp@1619: fp@1619: /** fp@1619: Stops the Ethernet-over-EtherCAT processing. fp@1619: */ fp@1619: fp@1619: void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */) fp@1619: { fp@1619: ec_eoe_t *eoe; fp@1619: fp@1619: if (!master->eoe_running) return; fp@1619: fp@1619: EC_INFO("Stopping EoE processing.\n"); fp@1619: fp@1619: del_timer_sync(&master->eoe_timer); fp@1619: fp@1619: // decouple all EoE handlers fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1619: if (eoe->slave) { fp@1619: eoe->slave->requested_state = EC_SLAVE_STATE_INIT; fp@1619: eoe->slave->state_error = 0; fp@1619: eoe->slave = NULL; fp@1619: } fp@1619: } fp@1619: fp@1619: master->eoe_running = 0; fp@1619: } fp@1619: fp@1619: /*****************************************************************************/ fp@1618: /** fp@1618: Does the Ethernet-over-EtherCAT processing. fp@1618: */ fp@1618: fp@1619: void ec_master_eoe_run(unsigned long data /**< master pointer */) fp@1619: { fp@1619: ec_master_t *master = (ec_master_t *) data; fp@1618: ec_eoe_t *eoe; fp@1619: unsigned int active = 0; fp@1619: fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1619: if (ec_eoe_active(eoe)) active++; fp@1619: } fp@1619: if (!active) goto queue_timer; fp@1619: fp@1619: // aquire master lock... fp@1619: if (master->mode == EC_MASTER_MODE_RUNNING) { fp@1619: // request_cb must return 0, if the lock has been aquired! fp@1619: if (master->request_cb(master->cb_data)) fp@1619: goto queue_timer; fp@1619: } fp@1619: else if (master->mode == EC_MASTER_MODE_FREERUN) { fp@1619: spin_lock(&master->internal_lock); fp@1619: } fp@1619: else fp@1619: goto queue_timer; fp@1619: fp@1619: // actual EoE stuff fp@1619: ecrt_master_async_receive(master); fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@1618: ec_eoe_run(eoe); fp@1618: } fp@1619: ecrt_master_async_send(master); fp@1619: fp@1619: // release lock... fp@1619: if (master->mode == EC_MASTER_MODE_RUNNING) { fp@1619: master->release_cb(master->cb_data); fp@1619: } fp@1619: else if (master->mode == EC_MASTER_MODE_FREERUN) { fp@1619: spin_unlock(&master->internal_lock); fp@1619: } fp@1619: fp@1619: queue_timer: fp@1619: master->eoe_timer.expires += HZ / EC_EOE_FREQUENCY; fp@1619: add_timer(&master->eoe_timer); fp@1618: } fp@1618: 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@1618: \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@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: int ecrt_master_activate(ec_master_t *master /**< EtherCAT master */) fp@73: { fp@182: unsigned int j; fp@73: ec_slave_t *slave; fp@144: ec_command_t *command; fp@73: const ec_sync_t *sync; fp@73: const ec_slave_type_t *type; fp@73: const ec_fmmu_t *fmmu; fp@73: uint32_t domain_offset; fp@84: ec_domain_t *domain; fp@155: ec_eeprom_sync_t *eeprom_sync, mbox_sync; fp@73: fp@144: command = &master->simple_command; 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@195: // configure and activate slaves fp@182: list_for_each_entry(slave, &master->slaves, list) { fp@73: fp@195: // change state to INIT fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_INIT))) fp@73: return -1; fp@73: fp@195: // check for slave registration fp@73: type = slave->type; fp@182: if (!type) fp@182: EC_WARN("Slave %i has unknown type!\n", slave->ring_position); fp@73: fp@195: // check and reset CRC fault counters fp@74: ec_slave_check_crc(slave); fp@74: fp@195: // reset FMMUs fp@73: if (slave->base_fmmu_count) { fp@144: if (ec_command_npwr(command, slave->station_address, 0x0600, fp@144: EC_FMMU_SIZE * slave->base_fmmu_count)) fp@144: return -1; fp@144: memset(command->data, 0x00, EC_FMMU_SIZE * slave->base_fmmu_count); fp@145: if (unlikely(ec_master_simple_io(master, command))) { fp@89: EC_ERR("Resetting FMMUs failed on slave %i!\n", fp@84: slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@195: // reset sync managers fp@73: if (slave->base_sync_count) { fp@144: if (ec_command_npwr(command, slave->station_address, 0x0800, fp@144: EC_SYNC_SIZE * slave->base_sync_count)) fp@144: return -1; fp@144: memset(command->data, 0x00, EC_SYNC_SIZE * slave->base_sync_count); fp@145: if (unlikely(ec_master_simple_io(master, command))) { fp@89: EC_ERR("Resetting sync managers failed on slave %i!\n", fp@84: slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@195: // configure sync managers fp@195: if (type) { // known slave type, take type's SM information fp@164: for (j = 0; type->sync_managers[j] && j < EC_MAX_SYNC; j++) { fp@136: sync = type->sync_managers[j]; fp@144: if (ec_command_npwr(command, slave->station_address, fp@144: 0x0800 + j * EC_SYNC_SIZE, EC_SYNC_SIZE)) fp@144: return -1; fp@1621: ec_sync_config(sync, slave, command->data); fp@145: if (unlikely(ec_master_simple_io(master, command))) { fp@136: EC_ERR("Setting sync manager %i failed on slave %i!\n", fp@136: j, slave->ring_position); fp@136: return -1; fp@136: } fp@136: } fp@136: } fp@195: else if (slave->sii_mailbox_protocols) { // unknown type, but mailbox fp@195: // does the device supply SM configurations in its EEPROM? fp@155: if (!list_empty(&slave->eeprom_syncs)) { fp@155: list_for_each_entry(eeprom_sync, &slave->eeprom_syncs, list) { fp@155: EC_INFO("Sync manager %i...\n", eeprom_sync->index); fp@155: if (ec_command_npwr(command, slave->station_address, fp@155: 0x800 + eeprom_sync->index * fp@155: EC_SYNC_SIZE, fp@155: EC_SYNC_SIZE)) return -1; fp@155: ec_eeprom_sync_config(eeprom_sync, command->data); fp@155: if (unlikely(ec_master_simple_io(master, command))) { fp@155: EC_ERR("Setting sync manager %i failed on slave %i!\n", fp@155: eeprom_sync->index, slave->ring_position); fp@155: return -1; fp@155: } fp@155: } fp@73: } fp@195: else { // no sync manager information; guess mailbox settings fp@155: mbox_sync.physical_start_address = fp@155: slave->sii_rx_mailbox_offset; fp@155: mbox_sync.length = slave->sii_rx_mailbox_size; fp@155: mbox_sync.control_register = 0x26; fp@155: mbox_sync.enable = 1; fp@155: if (ec_command_npwr(command, slave->station_address, fp@155: 0x800,EC_SYNC_SIZE)) return -1; fp@155: ec_eeprom_sync_config(&mbox_sync, command->data); fp@155: if (unlikely(ec_master_simple_io(master, command))) { fp@155: EC_ERR("Setting sync manager 0 failed on slave %i!\n", fp@155: slave->ring_position); fp@155: return -1; fp@155: } fp@155: fp@155: mbox_sync.physical_start_address = fp@155: slave->sii_tx_mailbox_offset; fp@155: mbox_sync.length = slave->sii_tx_mailbox_size; fp@155: mbox_sync.control_register = 0x22; fp@155: mbox_sync.enable = 1; fp@155: if (ec_command_npwr(command, slave->station_address, fp@155: 0x808, EC_SYNC_SIZE)) return -1; fp@155: ec_eeprom_sync_config(&mbox_sync, command->data); fp@155: if (unlikely(ec_master_simple_io(master, command))) { fp@155: EC_ERR("Setting sync manager 1 failed on slave %i!\n", fp@155: slave->ring_position); fp@155: return -1; fp@155: } fp@155: } fp@155: EC_INFO("Mailbox configured for unknown slave %i\n", fp@155: slave->ring_position); fp@73: } fp@73: fp@195: // change state to PREOP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_PREOP))) fp@73: return -1; fp@73: fp@195: // stop activation here for slaves without type fp@136: if (!type) continue; fp@136: fp@195: // slaves that are not registered are only brought into PREOP fp@145: // state -> nice blinking and mailbox communication possible fp@145: if (!slave->registered && !slave->type->special) { fp@84: EC_WARN("Slave %i was not registered!\n", slave->ring_position); fp@81: continue; fp@81: } fp@81: fp@195: // configure FMMUs fp@164: for (j = 0; j < slave->fmmu_count; j++) { fp@73: fmmu = &slave->fmmus[j]; fp@144: if (ec_command_npwr(command, slave->station_address, fp@144: 0x0600 + j * EC_FMMU_SIZE, EC_FMMU_SIZE)) fp@144: return -1; fp@1621: ec_fmmu_config(fmmu, slave, command->data); fp@145: if (unlikely(ec_master_simple_io(master, command))) { fp@89: EC_ERR("Setting FMMU %i failed on slave %i!\n", fp@89: j, slave->ring_position); fp@73: return -1; fp@73: } fp@73: } fp@73: fp@195: // change state to SAVEOP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_SAVEOP))) fp@73: return -1; fp@73: fp@195: // change state to OP fp@73: if (unlikely(ec_slave_state_change(slave, EC_SLAVE_STATE_OP))) fp@73: return -1; fp@73: } 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@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_deactivate(ec_master_t *master /**< EtherCAT master */) fp@73: { fp@73: ec_slave_t *slave; fp@182: fp@182: list_for_each_entry(slave, &master->slaves, list) { fp@74: ec_slave_check_crc(slave); fp@104: ec_slave_state_change(slave, EC_SLAVE_STATE_INIT); fp@104: } fp@104: } fp@104: fp@135: fp@135: /*****************************************************************************/ fp@135: fp@135: /** fp@195: Fetches the SDO dictionaries of all slaves. fp@195: Slaves that do not support the CoE protocol are left out. fp@195: \return 0 in case of success, else < 0 fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: int ecrt_master_fetch_sdo_lists(ec_master_t *master /**< EtherCAT master */) fp@135: { fp@135: ec_slave_t *slave; fp@182: fp@182: list_for_each_entry(slave, &master->slaves, list) { fp@135: if (slave->sii_mailbox_protocols & EC_MBOX_COE) { fp@135: if (unlikely(ec_slave_fetch_sdo_list(slave))) { fp@135: EC_ERR("Failed to fetch SDO list on slave %i!\n", fp@135: slave->ring_position); fp@135: return -1; fp@135: } fp@135: } fp@135: } fp@135: fp@135: return 0; fp@135: } fp@135: fp@104: /*****************************************************************************/ fp@104: fp@104: /** fp@195: Sends queued commands and waits for their reception. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_sync_io(ec_master_t *master /**< EtherCAT master */) fp@98: { fp@144: ec_command_t *command, *n; fp@98: unsigned int commands_sent; fp@98: cycles_t t_start, t_end, t_timeout; fp@98: fp@195: // send commands fp@144: ecrt_master_async_send(master); fp@98: fp@195: t_start = get_cycles(); // measure io time fp@120: t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); fp@98: fp@195: while (1) { // active waiting fp@98: ec_device_call_isr(master->device); fp@98: fp@195: t_end = get_cycles(); // take current time fp@195: if (t_end - t_start >= t_timeout) break; // timeout! fp@98: fp@98: commands_sent = 0; fp@144: list_for_each_entry_safe(command, n, &master->command_queue, queue) { fp@98: if (command->state == EC_CMD_RECEIVED) fp@144: list_del_init(&command->queue); fp@98: else if (command->state == EC_CMD_SENT) fp@98: commands_sent++; fp@98: } fp@113: fp@113: if (!commands_sent) break; fp@113: } fp@98: fp@195: // timeout; dequeue all commands fp@144: list_for_each_entry_safe(command, n, &master->command_queue, queue) { fp@98: switch (command->state) { fp@98: case EC_CMD_SENT: fp@98: case EC_CMD_QUEUED: fp@98: command->state = EC_CMD_TIMEOUT; fp@98: master->stats.timeouts++; fp@98: ec_master_output_stats(master); fp@98: break; fp@98: case EC_CMD_RECEIVED: fp@98: master->stats.delayed++; fp@98: ec_master_output_stats(master); fp@98: break; fp@98: default: fp@98: break; fp@98: } fp@144: list_del_init(&command->queue); fp@98: } fp@98: } fp@98: fp@98: /*****************************************************************************/ fp@98: fp@98: /** fp@195: Asynchronous sending of commands. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_async_send(ec_master_t *master /**< EtherCAT master */) fp@104: { fp@144: ec_command_t *command, *n; fp@104: fp@104: if (unlikely(!master->device->link_state)) { fp@195: // link is down, no command can be sent fp@144: list_for_each_entry_safe(command, n, &master->command_queue, queue) { fp@104: command->state = EC_CMD_ERROR; fp@144: list_del_init(&command->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@104: ec_master_send_commands(master); fp@104: } fp@104: fp@104: /*****************************************************************************/ fp@104: fp@104: /** fp@195: Asynchronous receiving of commands. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_async_receive(ec_master_t *master /**< EtherCAT master */) fp@104: { fp@104: ec_command_t *command, *next; fp@1619: cycles_t t_received, t_timeout; fp@104: fp@104: ec_device_call_isr(master->device); fp@104: fp@1619: t_received = get_cycles(); fp@1619: t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); fp@1619: fp@195: // dequeue all received commands fp@144: list_for_each_entry_safe(command, next, &master->command_queue, queue) fp@144: if (command->state == EC_CMD_RECEIVED) list_del_init(&command->queue); fp@104: fp@1619: // dequeue all commands that timed out fp@144: list_for_each_entry_safe(command, next, &master->command_queue, queue) { fp@104: switch (command->state) { fp@104: case EC_CMD_SENT: fp@104: case EC_CMD_QUEUED: fp@1619: if (t_received - command->t_sent > t_timeout) { fp@1619: list_del_init(&command->queue); fp@1619: command->state = EC_CMD_TIMEOUT; fp@1619: master->stats.timeouts++; fp@1619: ec_master_output_stats(master); fp@1619: } 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@195: Queues all domain commands and sends them. Then waits a certain time, so fp@195: that ecrt_master_sasync_receive() can be called securely. fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_prepare_async_io(ec_master_t *master /**< EtherCAT master */) fp@106: { fp@106: ec_domain_t *domain; fp@106: cycles_t t_start, t_end, t_timeout; fp@106: fp@195: // queue commands of all domains fp@106: list_for_each_entry(domain, &master->domains, list) fp@106: ecrt_domain_queue(domain); fp@106: fp@106: ecrt_master_async_send(master); fp@106: fp@195: t_start = get_cycles(); // take sending time fp@120: t_timeout = (cycles_t) master->timeout * (cpu_khz / 1000); fp@106: fp@195: // active waiting fp@113: while (1) { fp@106: t_end = get_cycles(); fp@113: if (t_end - t_start >= t_timeout) break; fp@113: } fp@106: } fp@106: fp@106: /*****************************************************************************/ fp@106: fp@106: /** fp@195: Does all cyclic master work. fp@1618: \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@1619: // execute master state machine fp@1619: ec_fsm_execute(&master->fsm); fp@144: } fp@144: fp@144: /*****************************************************************************/ fp@144: fp@144: /** fp@1618: Translates an ASCII coded bus-address to a slave pointer. fp@1618: These are the valid addressing schemes: fp@1618: - \a "X" = the X. slave on the bus, fp@1618: - \a "X:Y" = the Y. slave after the X. branch (bus coupler), fp@1618: - \a "#X" = the slave with alias X, fp@1618: - \a "#X:Y" = the Y. slave after the branch (bus coupler) with alias X. fp@1618: X and Y are zero-based indices and may be provided in hexadecimal or octal fp@1618: notation (with respective prefix). fp@1618: \return pointer to the slave on success, else NULL fp@1618: \ingroup RealtimeInterface fp@1618: */ fp@1618: fp@1618: ec_slave_t *ecrt_master_get_slave(const ec_master_t *master, /**< Master */ fp@1618: const char *address /**< address string */ fp@1618: ) fp@1618: { fp@1618: unsigned long first, second; fp@1618: char *remainder, *remainder2; fp@1618: unsigned int alias_requested, alias_found; fp@1618: ec_slave_t *alias_slave = NULL, *slave; fp@1618: fp@1618: if (!address || address[0] == 0) return NULL; fp@1618: fp@1618: alias_requested = 0; fp@1618: if (address[0] == '#') { fp@1618: alias_requested = 1; fp@1618: address++; fp@1618: } fp@1618: fp@1618: first = simple_strtoul(address, &remainder, 0); fp@1618: if (remainder == address) { fp@1618: EC_ERR("Slave address \"%s\" - First number empty!\n", address); fp@1618: return NULL; fp@1618: } fp@1618: fp@1618: if (alias_requested) { fp@1618: alias_found = 0; fp@1618: list_for_each_entry(alias_slave, &master->slaves, list) { fp@1618: if (alias_slave->sii_alias == first) { fp@1618: alias_found = 1; fp@1618: break; fp@1618: } fp@1618: } fp@1618: if (!alias_found) { fp@1618: EC_ERR("Slave address \"%s\" - Alias not found!\n", address); fp@1618: return NULL; fp@1618: } fp@1618: } fp@1618: fp@1618: if (!remainder[0]) { // absolute position fp@1618: if (alias_requested) { fp@1618: return alias_slave; fp@1618: } fp@1618: else { fp@1618: list_for_each_entry(slave, &master->slaves, list) { fp@1618: if (slave->ring_position == first) return slave; fp@1618: } fp@1618: EC_ERR("Slave address \"%s\" - Absolute position invalid!\n", fp@1618: address); fp@1618: } fp@1618: } fp@1618: else if (remainder[0] == ':') { // field position fp@1618: remainder++; fp@1618: second = simple_strtoul(remainder, &remainder2, 0); fp@1618: fp@1618: if (remainder2 == remainder) { fp@1618: EC_ERR("Slave address \"%s\" - Second number empty!\n", address); fp@1618: return NULL; fp@1618: } fp@1618: fp@1618: if (remainder2[0]) { fp@1618: EC_ERR("Slave address \"%s\" - Invalid trailer!\n", address); fp@1618: return NULL; fp@1618: } fp@1618: fp@1618: if (alias_requested) { fp@1618: if (!alias_slave->type || fp@1618: alias_slave->type->special != EC_TYPE_BUS_COUPLER) { fp@1618: EC_ERR("Slave address \"%s\": Alias slave must be bus coupler" fp@1618: " in colon mode.\n", address); fp@1618: return NULL; fp@1618: } fp@1618: list_for_each_entry(slave, &master->slaves, list) { fp@1618: if (slave->coupler_index == alias_slave->coupler_index fp@1618: && slave->coupler_subindex == second) fp@1618: return slave; fp@1618: } fp@1618: EC_ERR("Slave address \"%s\" - Bus coupler %i has no %lu. slave" fp@1618: " following!\n", address, alias_slave->ring_position, fp@1618: second); fp@1618: return NULL; fp@1618: } fp@1618: else { fp@1618: list_for_each_entry(slave, &master->slaves, list) { fp@1618: if (slave->coupler_index == first fp@1618: && slave->coupler_subindex == second) return slave; fp@1618: } fp@1618: } fp@1618: } fp@1618: else fp@1618: EC_ERR("Slave address \"%s\" - Invalid format!\n", address); fp@1618: fp@1618: return NULL; fp@1618: } fp@1618: fp@1618: /*****************************************************************************/ fp@1618: fp@1618: /** fp@1619: Sets the locking callbacks. fp@1619: The request_cb function must return zero, to allow another instance fp@1619: (the EoE process for example) to access the master. Non-zero means, fp@1619: that access is forbidden at this time. fp@1619: \ingroup RealtimeInterface fp@1619: */ fp@1619: fp@1619: void ecrt_master_callbacks(ec_master_t *master, /**< EtherCAT master */ fp@1619: int (*request_cb)(void *), /**< request lock CB */ fp@1619: void (*release_cb)(void *), /**< release lock CB */ fp@1619: void *cb_data /**< data parameter */ fp@1619: ) fp@1619: { fp@1619: master->request_cb = request_cb; fp@1619: master->release_cb = release_cb; fp@1619: master->cb_data = cb_data; fp@1619: } fp@1619: fp@1619: /*****************************************************************************/ fp@1619: fp@1619: /** fp@1619: Starts Ethernet-over-EtherCAT processing for all EoE-capable slaves. fp@1619: \ingroup RealtimeInterface fp@1619: */ fp@1619: fp@1619: int ecrt_master_start_eoe(ec_master_t *master /**< EtherCAT master */) fp@1619: { fp@1619: if (!master->request_cb || !master->release_cb) { fp@1619: EC_ERR("EoE requires master callbacks to be set!\n"); fp@1619: return -1; fp@1619: } fp@1619: fp@1619: ec_master_eoe_start(master); fp@1619: return 0; fp@1619: } fp@1619: fp@1619: /*****************************************************************************/ fp@1619: fp@1619: /** fp@195: Sets the debug level of the master. fp@195: The following levels are valid: fp@195: - 1: only output positions marks and basic data fp@195: - 2: additional frame data output fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_debug(ec_master_t *master, /**< EtherCAT master */ fp@195: int level /**< debug level */ fp@104: ) fp@73: { fp@98: if (level != master->debug_level) { fp@98: master->debug_level = level; fp@98: EC_INFO("Master debug level set to %i.\n", level); fp@98: } fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@73: /** fp@195: Outputs all master information. fp@140: Verbosity: fp@195: - 0: Only slave types and positions fp@195: - 1: with EEPROM contents fp@195: - >1: with SDO dictionaries fp@1618: \ingroup RealtimeInterface fp@195: */ fp@195: fp@195: void ecrt_master_print(const ec_master_t *master, /**< EtherCAT master */ fp@195: unsigned int verbosity /**< verbosity level */ fp@140: ) fp@73: { fp@182: ec_slave_t *slave; fp@145: ec_eoe_t *eoe; fp@73: fp@84: EC_INFO("*** Begin master information ***\n"); fp@145: if (master->slave_count) { fp@145: EC_INFO("Slave list:\n"); fp@182: list_for_each_entry(slave, &master->slaves, list) fp@182: ec_slave_print(slave, verbosity); fp@145: } fp@1619: if (!list_empty(&master->eoe_handlers)) { fp@145: EC_INFO("Ethernet-over-EtherCAT (EoE) objects:\n"); fp@1619: list_for_each_entry(eoe, &master->eoe_handlers, list) { fp@145: ec_eoe_print(eoe); fp@145: } fp@145: } fp@84: EC_INFO("*** End master information ***\n"); fp@73: } fp@73: fp@73: /*****************************************************************************/ fp@73: fp@1618: /** \cond */ fp@145: fp@104: EXPORT_SYMBOL(ecrt_master_create_domain); fp@104: EXPORT_SYMBOL(ecrt_master_activate); fp@104: EXPORT_SYMBOL(ecrt_master_deactivate); fp@135: EXPORT_SYMBOL(ecrt_master_fetch_sdo_lists); fp@109: EXPORT_SYMBOL(ecrt_master_prepare_async_io); fp@104: EXPORT_SYMBOL(ecrt_master_sync_io); fp@104: EXPORT_SYMBOL(ecrt_master_async_send); fp@104: EXPORT_SYMBOL(ecrt_master_async_receive); fp@144: EXPORT_SYMBOL(ecrt_master_run); fp@1619: EXPORT_SYMBOL(ecrt_master_callbacks); fp@1619: EXPORT_SYMBOL(ecrt_master_start_eoe); fp@104: EXPORT_SYMBOL(ecrt_master_debug); fp@104: EXPORT_SYMBOL(ecrt_master_print); fp@138: EXPORT_SYMBOL(ecrt_master_get_slave); fp@42: fp@1618: /** \endcond */ fp@1618: fp@1618: /*****************************************************************************/