From ba2781922162eca6c10c7cc72a7b4e52e3359f26 Mon Sep 17 00:00:00 2001 From: Knud Baastrup Date: Thu, 21 Aug 2014 15:11:28 +0200 Subject: [PATCH] Applied base-0024-Sdo-directory-now-only-fetched-on-request Sdo directory now only fetched on request. The time-consuming SDO directory fetch during slave scan can now be skipped by setting the EC_SKIP_SDO_DICT in globals.h. The directory will now instead be fethed the first time an ethercat sdos command is executed with the ethercat tool. --- configure.ac | 29 ++++++++++ master/Kbuild.in | 3 +- master/Makefile.am | 3 +- master/dict_request.c | 62 +++++++++++++++++++++ master/dict_request.h | 60 ++++++++++++++++++++ master/fsm_master.c | 4 ++ master/fsm_slave.c | 122 +++++++++++++++++++++++++++++++++++++++++ master/fsm_slave.h | 2 + master/ioctl.c | 25 +++++++++ master/ioctl.h | 10 +++- master/master.c | 54 ++++++++++++++++++ master/master.h | 2 + master/slave.c | 10 ++++ master/slave.h | 1 + tool/CommandSdos.cpp | 15 +++++ tool/CommandUpload.cpp | 6 +- tool/MasterDevice.cpp | 11 ++++ tool/MasterDevice.h | 1 + 18 files changed, 415 insertions(+), 5 deletions(-) create mode 100644 master/dict_request.c create mode 100644 master/dict_request.h diff --git a/configure.ac b/configure.ac index a0483210..1d93e77f 100644 --- a/configure.ac +++ b/configure.ac @@ -1185,6 +1185,35 @@ esac AM_CONDITIONAL(HAVE_SYSTEMD, test "x$with_systemdsystemunitdir" != "x") AC_SUBST(systemdsystemunitdir,[$with_systemdsystemunitdir]) +#------------------------------------------------------------------------------ +# Skip SDO dictionary during slave scan +#------------------------------------------------------------------------------ + +AC_MSG_CHECKING([whether to skip SDO dictionary during slave scan]) + +AC_ARG_ENABLE([skip-sdo-dict], + AS_HELP_STRING([--enable-skip-sdo-dict], + [Enable skip SDO dictionary (default: no)]), + [ + case "${enableval}" in + yes) skipsdodict=1 + ;; + no) skipsdodict=0 + ;; + *) AC_MSG_ERROR([Invalid value for --enable-skip-sdo-dict]) + ;; + esac + ], + [skipsdodict=0] +) + +if test "x${skipsdodict}" = "x1"; then + AC_DEFINE([EC_SKIP_SDO_DICT], [1], [Skip SDO dictionary during slave scan]) + AC_MSG_RESULT([yes]) +else + AC_MSG_RESULT([no]) +fi + #------------------------------------------------------------------------------ AC_CONFIG_FILES([ diff --git a/master/Kbuild.in b/master/Kbuild.in index f2bb094c..507c45e3 100644 --- a/master/Kbuild.in +++ b/master/Kbuild.in @@ -71,7 +71,8 @@ ec_master-objs := \ soe_request.o \ sync.o \ sync_config.o \ - voe_handler.o + voe_handler.o \ + dict_request.o ifeq (@ENABLE_EOE@,1) ec_master-objs += eoe_request.o diff --git a/master/Makefile.am b/master/Makefile.am index 382fab77..32300e7d 100644 --- a/master/Makefile.am +++ b/master/Makefile.am @@ -76,6 +76,7 @@ noinst_HEADERS = \ soe_request.c soe_request.h \ sync.c sync.h \ sync_config.c sync_config.h \ - voe_handler.c voe_handler.h + voe_handler.c voe_handler.h \ + dict_request.c dict_request.h #------------------------------------------------------------------------------ diff --git a/master/dict_request.c b/master/dict_request.c new file mode 100644 index 00000000..b7ee66b7 --- /dev/null +++ b/master/dict_request.c @@ -0,0 +1,62 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * + * This file is part of the IgH EtherCAT Master. + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT Master is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT Master; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * --- + * + * The license mentioned above concerns the source code only. Using the + * EtherCAT technology and brand is only permitted in compliance with the + * industrial property and similar rights of Beckhoff Automation GmbH. + * + *****************************************************************************/ + +/** \file + * Canopen over EtherCAT dictionary request functions. + */ + +/*****************************************************************************/ + +#include "dict_request.h" + + +/*****************************************************************************/ + +/** Dictionary request constructor. + */ +void ec_dict_request_init(ec_dict_request_t *req) +{ + req->state = EC_INT_REQUEST_INIT; +} + +/*****************************************************************************/ + +void ec_dict_request_read(ec_dict_request_t *req) +{ + req->state = EC_INT_REQUEST_QUEUED; +} + +/*****************************************************************************/ + +/** \cond */ + + +/** \endcond */ + +/*****************************************************************************/ diff --git a/master/dict_request.h b/master/dict_request.h new file mode 100644 index 00000000..7228cc9b --- /dev/null +++ b/master/dict_request.h @@ -0,0 +1,60 @@ +/****************************************************************************** + * + * $Id$ + * + * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * + * This file is part of the IgH EtherCAT Master. + * + * The IgH EtherCAT Master is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License version 2, as + * published by the Free Software Foundation. + * + * The IgH EtherCAT Master is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General + * Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with the IgH EtherCAT Master; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * --- + * + * The license mentioned above concerns the source code only. Using the + * EtherCAT technology and brand is only permitted in compliance with the + * industrial property and similar rights of Beckhoff Automation GmbH. + * + *****************************************************************************/ + +/** + \file + EtherCAT CANopen dictionary request structure. +*/ + +/*****************************************************************************/ + +#ifndef __EC_DICT_REQUEST_H__ +#define __EC_DICT_REQUEST_H__ + +#include + +#include "globals.h" + +/*****************************************************************************/ + +/** CANopen dictionary request. + */ +typedef struct { + struct list_head list; /**< List item. */ + ec_internal_request_state_t state; /**< SDO request state. */ +} ec_dict_request_t; + +/*****************************************************************************/ + +void ec_dict_request_init(ec_dict_request_t *); +void ec_dict_request_read(ec_dict_request_t *); + +/*****************************************************************************/ + +#endif diff --git a/master/fsm_master.c b/master/fsm_master.c index 47ddb94d..caeb5d53 100644 --- a/master/fsm_master.c +++ b/master/fsm_master.c @@ -584,6 +584,7 @@ void ec_fsm_master_action_idle( for (slave = master->slaves; slave < master->slaves + master->slave_count; slave++) { +#ifndef EC_SKIP_SDO_DICT if (!(slave->sii.mailbox_protocols & EC_MBOX_COE) || (slave->sii.has_general && !slave->sii.coe_details.enable_sdo_info) @@ -596,8 +597,10 @@ void ec_fsm_master_action_idle( || (slave->sii.has_general && !slave->sii.coe_details.enable_sdo_info) ){ +#endif // SDO info not supported. Enable processing of requests ec_fsm_slave_set_ready(&slave->fsm); +#ifndef EC_SKIP_SDO_DICT } continue; } @@ -614,6 +617,7 @@ void ec_fsm_master_action_idle( ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram); // execute immediately fsm->datagram->device_index = fsm->slave->device_index; return; +#endif } // check for pending SII write operations. diff --git a/master/fsm_slave.c b/master/fsm_slave.c index ac09aeda..dedd06fb 100644 --- a/master/fsm_slave.c +++ b/master/fsm_slave.c @@ -56,6 +56,8 @@ void ec_fsm_slave_state_soe_request(ec_fsm_slave_t *, ec_datagram_t *); int ec_fsm_slave_action_process_eoe(ec_fsm_slave_t *, ec_datagram_t *); void ec_fsm_slave_state_eoe_request(ec_fsm_slave_t *, ec_datagram_t *); #endif +int ec_fsm_slave_action_process_dict(ec_fsm_slave_t *, ec_datagram_t *); +void ec_fsm_slave_state_dict_request(ec_fsm_slave_t *, ec_datagram_t *); /*****************************************************************************/ @@ -249,6 +251,11 @@ void ec_fsm_slave_state_ready( return; } #endif + + // Check for pending dictionary requests + if (ec_fsm_slave_action_process_dict(fsm, datagram)) { + return; + } } /*****************************************************************************/ @@ -618,6 +625,121 @@ void ec_fsm_slave_state_soe_request( fsm->state = ec_fsm_slave_state_ready; } +/*****************************************************************************/ + +/** Check for pending dictionary requests and process one. + * + * \return non-zero, if a request is processed. + */ +int ec_fsm_slave_action_process_dict( + ec_fsm_slave_t *fsm, /**< Slave state machine. */ + ec_datagram_t *datagram /**< Datagram to use. */ + ) +{ + ec_slave_t *slave = fsm->slave; + ec_dict_request_t *request; + + if (list_empty(&slave->dict_requests)) { + return 0; + } + + // take the first request to be processed + request = list_entry(slave->dict_requests.next, ec_dict_request_t, list); + list_del_init(&request->list); // dequeue + + if (!(slave->sii.mailbox_protocols & EC_MBOX_COE) + || (slave->sii.has_general + && !slave->sii.coe_details.enable_sdo_info)) + { + EC_SLAVE_INFO(slave, "Aborting dictionary request," + " slave does not support SDO Info.\n"); + request->state = EC_INT_REQUEST_SUCCESS; + wake_up_all(&slave->master->request_queue); + fsm->dict_request = NULL; + fsm->state = ec_fsm_slave_state_ready; + return 1; + } + + if (slave->sdo_dictionary_fetched) + { + EC_SLAVE_DBG(slave, 1, "Aborting dictionary request," + " dictionary already uploaded.\n"); + request->state = EC_INT_REQUEST_SUCCESS; + wake_up_all(&slave->master->request_queue); + fsm->dict_request = NULL; + fsm->state = ec_fsm_slave_state_ready; + return 1; + } + + if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) { + EC_SLAVE_WARN(slave, "Aborting dictionary request," + " slave has error flag set.\n"); + request->state = EC_INT_REQUEST_FAILURE; + wake_up_all(&slave->master->request_queue); + fsm->state = ec_fsm_slave_state_idle; + return 1; + } + + if (slave->current_state == EC_SLAVE_STATE_INIT) { + EC_SLAVE_WARN(slave, "Aborting dictioanry request, slave is in INIT.\n"); + request->state = EC_INT_REQUEST_FAILURE; + wake_up_all(&slave->master->request_queue); + fsm->state = ec_fsm_slave_state_idle; + return 1; + } + + fsm->dict_request = request; + request->state = EC_INT_REQUEST_BUSY; + + // Found pending dictionary request. Execute it! + EC_SLAVE_DBG(slave, 1, "Processing dictionary request...\n"); + + // Start dictionary transfer + fsm->state = ec_fsm_slave_state_dict_request; + ec_fsm_coe_dictionary(&fsm->fsm_coe, slave); + ec_fsm_coe_exec(&fsm->fsm_coe, datagram); // execute immediately + return 1; +} + +/*****************************************************************************/ + +/** Slave state: DICT_REQUEST. + */ +void ec_fsm_slave_state_dict_request( + ec_fsm_slave_t *fsm, /**< Slave state machine. */ + ec_datagram_t *datagram /**< Datagram to use. */ + ) +{ + ec_slave_t *slave = fsm->slave; + ec_dict_request_t *request = fsm->dict_request; + + if (ec_fsm_coe_exec(&fsm->fsm_coe, datagram)) { + return; + } + + if (!ec_fsm_coe_success(&fsm->fsm_coe)) { + EC_SLAVE_ERR(slave, "Failed to process dictionary request.\n"); + request->state = EC_INT_REQUEST_FAILURE; + wake_up_all(&slave->master->request_queue); + fsm->dict_request = NULL; + fsm->state = ec_fsm_slave_state_ready; + return; + } + + EC_SLAVE_DBG(slave, 1, "Finished dictionary request.\n"); + + // Dictionary request finished + slave->sdo_dictionary_fetched = 1; + + // attach pdo names from dictionary + ec_slave_attach_pdo_names(slave); + + request->state = EC_INT_REQUEST_SUCCESS; + wake_up_all(&slave->master->request_queue); + fsm->dict_request = NULL; + fsm->state = ec_fsm_slave_state_ready; +} + /*****************************************************************************/ #ifdef EC_EOE /** Check for pending EoE IP parameter requests and process one. diff --git a/master/fsm_slave.h b/master/fsm_slave.h index acc9ee2e..ae0a63a9 100644 --- a/master/fsm_slave.h +++ b/master/fsm_slave.h @@ -42,6 +42,7 @@ #include "sdo_request.h" #include "reg_request.h" #include "eoe_request.h" +#include "dict_request.h" #include "fsm_coe.h" #include "fsm_foe.h" #include "fsm_soe.h" @@ -69,6 +70,7 @@ struct ec_fsm_slave { #ifdef EC_EOE ec_eoe_request_t *eoe_request; /**< EoE request to process. */ #endif + ec_dict_request_t *dict_request; /**< Dictionary request to process. */ ec_fsm_coe_t fsm_coe; /**< CoE state machine. */ ec_fsm_foe_t fsm_foe; /**< FoE state machine. */ diff --git a/master/ioctl.c b/master/ioctl.c index c3b7896f..a768854e 100755 --- a/master/ioctl.c +++ b/master/ioctl.c @@ -4622,6 +4622,28 @@ static ATTRIBUTES int ec_ioctl_slave_soe_write( return retval; } +/*****************************************************************************/ +/** Upload Dictionary. + * + * \return Zero on success, otherwise a negative error code. + */ +static ATTRIBUTES int ec_ioctl_slave_dict_upload( + ec_master_t *master, /**< EtherCAT master. */ + void *arg /**< ioctl() argument. */ + ) +{ + ec_ioctl_slave_dict_upload_t data; + int ret; + + if (copy_from_user(&data, (void __user *) arg, sizeof(data))) { + return -EFAULT; + } + + ret = ec_master_dict_upload(master, data.slave_position); + + return ret; +} + /*****************************************************************************/ /** ioctl() function to use. @@ -5196,6 +5218,9 @@ long EC_IOCTL( } ret = ec_ioctl_set_send_interval(master, arg, ctx); break; + case EC_IOCTL_SLAVE_DICT_UPLOAD: + ret = ec_ioctl_slave_dict_upload(master, arg); + break; default: ret = -ENOTTY; break; diff --git a/master/ioctl.h b/master/ioctl.h index cec73aba..32161715 100644 --- a/master/ioctl.h +++ b/master/ioctl.h @@ -47,7 +47,7 @@ * * Increment this when changing the ioctl interface! */ -#define EC_IOCTL_VERSION_MAGIC 38 +#define EC_IOCTL_VERSION_MAGIC 39 // Command-line tool #define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t) @@ -86,6 +86,7 @@ #ifdef EC_EOE #define EC_IOCTL_EOE_HANDLER EC_IOWR(0x1f, ec_ioctl_eoe_handler_t) #endif +#define EC_IOCTL_SLAVE_DICT_UPLOAD EC_IOW(0x7f, ec_ioctl_slave_dict_upload_t) // Application interface #define EC_IOCTL_REQUEST EC_IO(0x20) @@ -812,6 +813,13 @@ typedef struct { /*****************************************************************************/ +typedef struct { + // inputs + uint16_t slave_position; +} ec_ioctl_slave_dict_upload_t; + +/*****************************************************************************/ + #ifdef __KERNEL__ /** Context data structure for file handles. diff --git a/master/master.c b/master/master.c index af28eea5..52db0400 100644 --- a/master/master.c +++ b/master/master.c @@ -2424,6 +2424,60 @@ void ec_master_request_op( #endif } +/*****************************************************************************/ + +int ec_master_dict_upload(ec_master_t *master, uint16_t slave_position) +{ + ec_dict_request_t request; + ec_slave_t *slave; + int ret = 0; + + EC_MASTER_DBG(master, 1, "%s(master = 0x%p, slave_position = %u\n", + __func__, master, slave_position); + + ec_dict_request_init(&request); + ec_dict_request_read(&request); + + if (ec_lock_down_interruptible(&master->master_sem)) { + return -EINTR; + } + + if (!(slave = ec_master_find_slave(master, 0, slave_position))) { + ec_lock_up(&master->master_sem); + EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position); + return -EINVAL; + } + + EC_SLAVE_DBG(slave, 1, "Scheduling dictionary upload request.\n"); + + // schedule request. + list_add_tail(&request.list, &slave->dict_requests); + + ec_lock_up(&master->master_sem); + + // wait for processing through FSM + if (wait_event_interruptible(master->request_queue, + request.state != EC_INT_REQUEST_QUEUED)) { + // interrupted by signal + ec_lock_down(&master->master_sem); + if (request.state == EC_INT_REQUEST_QUEUED) { + list_del(&request.list); + ec_lock_up(&master->master_sem); + return -EINTR; + } + // request already processing: interrupt not possible. + ec_lock_up(&master->master_sem); + } + + // wait until master FSM has finished processing + wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY); + + if (request.state != EC_INT_REQUEST_SUCCESS) { + ret = -EIO; + } + return ret; +} + /****************************************************************************** * Application interface *****************************************************************************/ diff --git a/master/master.h b/master/master.h index 9541df5b..9ecfc8e2 100644 --- a/master/master.h +++ b/master/master.h @@ -384,6 +384,8 @@ void ec_master_request_op(ec_master_t *); void ec_master_internal_send_cb(void *); void ec_master_internal_receive_cb(void *); +int ec_master_dict_upload(ec_master_t *, uint16_t); + extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c /*****************************************************************************/ diff --git a/master/slave.c b/master/slave.c index 5a61d99d..22844a8d 100644 --- a/master/slave.c +++ b/master/slave.c @@ -161,6 +161,7 @@ void ec_slave_init( INIT_LIST_HEAD(&slave->foe_requests); INIT_LIST_HEAD(&slave->soe_requests); INIT_LIST_HEAD(&slave->eoe_requests); + INIT_LIST_HEAD(&slave->dict_requests); // create state machine object ec_fsm_slave_init(&slave->fsm, slave); @@ -273,6 +274,15 @@ void ec_slave_clear(ec_slave_t *slave /**< EtherCAT slave */) request->state = EC_INT_REQUEST_FAILURE; } + while (!list_empty(&slave->dict_requests)) { + ec_dict_request_t *request = + list_entry(slave->dict_requests.next, ec_dict_request_t, list); + list_del_init(&request->list); // dequeue + EC_SLAVE_WARN(slave, "Discarding dictionary request," + " slave about to be deleted.\n"); + request->state = EC_INT_REQUEST_FAILURE; + } + wake_up_all(&slave->master->request_queue); if (slave->config) { diff --git a/master/slave.h b/master/slave.h index 6fe5da5c..5c85a048 100644 --- a/master/slave.h +++ b/master/slave.h @@ -254,6 +254,7 @@ struct ec_slave struct list_head foe_requests; /**< FoE requests. */ struct list_head soe_requests; /**< SoE requests. */ struct list_head eoe_requests; /**< EoE set IP parameter requests. */ + struct list_head dict_requests; /**< Dictionary read requests. */ ec_fsm_slave_t fsm; /**< Slave state machine. */ diff --git a/tool/CommandSdos.cpp b/tool/CommandSdos.cpp index a4d842fc..197def4e 100644 --- a/tool/CommandSdos.cpp +++ b/tool/CommandSdos.cpp @@ -94,6 +94,8 @@ void CommandSdos::execute(const StringVector &args) SlaveList::const_iterator si; bool showHeader, multiMaster; + ec_ioctl_slave_dict_upload_t data; + if (args.size()) { stringstream err; err << "'" << getName() << "' takes no arguments!"; @@ -108,6 +110,19 @@ void CommandSdos::execute(const StringVector &args) MasterDevice m(*mi); m.open(MasterDevice::Read); slaves = selectedSlaves(m); + for (si = slaves.begin(); si != slaves.end(); si++) { + if (si->coe_details.enable_sdo_info) { + data.slave_position = si->position; + try { + m.dictUpload(&data); + } catch (MasterDeviceException &e) { + throw e; + } + } + } + m.close(); + m.open(MasterDevice::Read); + slaves = selectedSlaves(m); showHeader = multiMaster || slaves.size() > 1; for (si = slaves.begin(); si != slaves.end(); si++) { diff --git a/tool/CommandUpload.cpp b/tool/CommandUpload.cpp index 439eeab3..affe52ac 100644 --- a/tool/CommandUpload.cpp +++ b/tool/CommandUpload.cpp @@ -134,13 +134,15 @@ void CommandUpload::execute(const StringVector &args) data.sdo_index, data.sdo_entry_subindex); } catch (MasterDeviceException &e) { err << "Failed to determine SDO entry data type. " - << "Please specify --type."; + << "Please specify --type or fetch directory " + << "with ethercat sdos command."; throwCommandException(err); } if (!(dataType = findDataType(entry.data_type))) { err << "PDO entry has unknown data type 0x" << hex << setfill('0') << setw(4) << entry.data_type << "!" - << " Please specify --type."; + << " Please specify --type or fetch directory " + << "with ethercat sdos command.";; throwCommandException(err); } } diff --git a/tool/MasterDevice.cpp b/tool/MasterDevice.cpp index d5faa524..b9e2c473 100644 --- a/tool/MasterDevice.cpp +++ b/tool/MasterDevice.cpp @@ -536,6 +536,17 @@ void MasterDevice::sdoUpload(ec_ioctl_slave_sdo_upload_t *data) /****************************************************************************/ +void MasterDevice::dictUpload(ec_ioctl_slave_dict_upload_t *data) +{ + if (ioctl(fd, EC_IOCTL_SLAVE_DICT_UPLOAD, data) < 0) { + stringstream err; + err << "Failed to upload dictionary: " << strerror(errno); + throw MasterDeviceException(err); + } +} + +/****************************************************************************/ + void MasterDevice::requestState( uint16_t slavePosition, uint8_t state diff --git a/tool/MasterDevice.h b/tool/MasterDevice.h index 0684fc9e..9958a413 100644 --- a/tool/MasterDevice.h +++ b/tool/MasterDevice.h @@ -164,6 +164,7 @@ class MasterDevice #endif void readSoe(ec_ioctl_slave_soe_read_t *); void writeSoe(ec_ioctl_slave_soe_write_t *); + void dictUpload(ec_ioctl_slave_dict_upload_t *); unsigned int getMasterCount() const {return masterCount;}