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_coe.c b/master/fsm_coe.c index 1d3f4719..f9620c34 100644 --- a/master/fsm_coe.c +++ b/master/fsm_coe.c @@ -230,35 +230,34 @@ void ec_fsm_coe_transfer( /** Executes the current state of the state machine. * - * \return 1 if the datagram was used, else 0. + * \return 1 if the state machine is still in progress, else 0. */ int ec_fsm_coe_exec( ec_fsm_coe_t *fsm, /**< Finite state machine. */ ec_datagram_t *datagram /**< Datagram to use. */ ) { - int datagram_used = 0; - + if (fsm->state == ec_fsm_coe_end || fsm->state == ec_fsm_coe_error) + return 0; if (fsm->datagram && (fsm->datagram->state == EC_DATAGRAM_INIT || fsm->datagram->state == EC_DATAGRAM_QUEUED || fsm->datagram->state == EC_DATAGRAM_SENT)) { // datagram not received yet - return datagram_used; + if (datagram != fsm->datagram) + datagram->state = EC_DATAGRAM_INVALID; + return 1; } fsm->state(fsm, datagram); - datagram_used = - fsm->state != ec_fsm_coe_end && fsm->state != ec_fsm_coe_error; - - if (datagram_used) { - fsm->datagram = datagram; - } else { + if (fsm->state == ec_fsm_coe_end || fsm->state == ec_fsm_coe_error) { fsm->datagram = NULL; + return 0; } - return datagram_used; + fsm->datagram = datagram; + return 1; } /*****************************************************************************/ diff --git a/master/fsm_eoe.c b/master/fsm_eoe.c index d1f3dc23..a4a37ec1 100644 --- a/master/fsm_eoe.c +++ b/master/fsm_eoe.c @@ -101,35 +101,35 @@ void ec_fsm_eoe_set_ip_param( /** Executes the current state of the state machine. * - * \return 1 if the datagram was used, else 0. + * \return 1 if the state machine is still in progress, else 0. */ int ec_fsm_eoe_exec( ec_fsm_eoe_t *fsm, /**< finite state machine */ ec_datagram_t *datagram /**< Datagram to use. */ ) { - int datagram_used = 0; + if (fsm->state == ec_fsm_eoe_end || fsm->state == ec_fsm_eoe_error) + return 0; if (fsm->datagram && (fsm->datagram->state == EC_DATAGRAM_INIT || fsm->datagram->state == EC_DATAGRAM_QUEUED || fsm->datagram->state == EC_DATAGRAM_SENT)) { // datagram not received yet - return datagram_used; + if (datagram != fsm->datagram) + datagram->state = EC_DATAGRAM_INVALID; + return 1; } fsm->state(fsm, datagram); - datagram_used = - fsm->state != ec_fsm_eoe_end && fsm->state != ec_fsm_eoe_error; - - if (datagram_used) { - fsm->datagram = datagram; - } else { + if (fsm->state == ec_fsm_eoe_end || fsm->state == ec_fsm_eoe_error) { fsm->datagram = NULL; + return 0; } - return datagram_used; + fsm->datagram = datagram; + return 1; } /*****************************************************************************/ diff --git a/master/fsm_foe.c b/master/fsm_foe.c index debd17c9..1ce7b7aa 100644 --- a/master/fsm_foe.c +++ b/master/fsm_foe.c @@ -123,35 +123,35 @@ void ec_fsm_foe_clear(ec_fsm_foe_t *fsm /**< finite state machine */) /** Executes the current state of the state machine. * - * \return 1, if the datagram was used, else 0. + * \return 1 if the state machine is still in progress, else 0. */ int ec_fsm_foe_exec( ec_fsm_foe_t *fsm, /**< finite state machine */ ec_datagram_t *datagram /**< Datagram to use. */ ) { - int datagram_used = 0; + if (fsm->state == ec_fsm_foe_end || fsm->state == ec_fsm_foe_error) + return 0; if (fsm->datagram && (fsm->datagram->state == EC_DATAGRAM_INIT || fsm->datagram->state == EC_DATAGRAM_QUEUED || fsm->datagram->state == EC_DATAGRAM_SENT)) { // datagram not received yet - return datagram_used; + if (datagram != fsm->datagram) + datagram->state = EC_DATAGRAM_INVALID; + return 1; } fsm->state(fsm, datagram); - datagram_used = - fsm->state != ec_fsm_foe_end && fsm->state != ec_fsm_foe_error; - - if (datagram_used) { - fsm->datagram = datagram; - } else { + if (fsm->state == ec_fsm_foe_end || fsm->state == ec_fsm_foe_error) { fsm->datagram = NULL; + return 0; } - return datagram_used; + fsm->datagram = datagram; + return 1; } /*****************************************************************************/ diff --git a/master/fsm_master.c b/master/fsm_master.c index c1cb5b46..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. @@ -1135,6 +1139,9 @@ void ec_fsm_master_state_scan_slave( if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan)) { return; } + // Assume that the slaves mailbox data is valid even if the slave scanning skipped + // the clear mailbox state, e.g. if the slave refused to enter state INIT. + fsm->slave->valid_mbox_data = 1; #ifdef EC_EOE if (slave->sii.mailbox_protocols & EC_MBOX_EOE) { 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/fsm_slave_scan.c b/master/fsm_slave_scan.c index 6637a285..643419c6 100644 --- a/master/fsm_slave_scan.c +++ b/master/fsm_slave_scan.c @@ -55,6 +55,7 @@ void ec_fsm_slave_scan_state_assign_sii(ec_fsm_slave_scan_t *); #endif void ec_fsm_slave_scan_state_sii_size(ec_fsm_slave_scan_t *); void ec_fsm_slave_scan_state_sii_data(ec_fsm_slave_scan_t *); +void ec_fsm_slave_scan_state_mailbox_cleared(ec_fsm_slave_scan_t *); #ifdef EC_REGALIAS void ec_fsm_slave_scan_state_regalias(ec_fsm_slave_scan_t *); #endif @@ -70,6 +71,7 @@ void ec_fsm_slave_scan_enter_datalink(ec_fsm_slave_scan_t *); void ec_fsm_slave_scan_enter_regalias(ec_fsm_slave_scan_t *); #endif void ec_fsm_slave_scan_enter_preop(ec_fsm_slave_scan_t *); +void ec_fsm_slave_scan_enter_clear_mailbox(ec_fsm_slave_scan_t *); void ec_fsm_slave_scan_enter_pdos(ec_fsm_slave_scan_t *); /*****************************************************************************/ @@ -774,6 +776,23 @@ void ec_fsm_slave_scan_state_sii_data(ec_fsm_slave_scan_t *fsm " Disabling mailbox communication."); } + // clear mailbox settings if invalid values due to invalid sii file + if ((slave->sii.boot_rx_mailbox_offset == 0xFFFF) || + (slave->sii.boot_tx_mailbox_offset == 0xFFFF) || + (slave->sii.std_rx_mailbox_offset == 0xFFFF) || + (slave->sii.std_tx_mailbox_offset == 0xFFFF)) { + slave->sii.boot_rx_mailbox_offset = 0; + slave->sii.boot_tx_mailbox_offset = 0; + slave->sii.boot_rx_mailbox_size = 0; + slave->sii.boot_tx_mailbox_size = 0; + slave->sii.std_rx_mailbox_offset = 0; + slave->sii.std_tx_mailbox_offset = 0; + slave->sii.std_rx_mailbox_size = 0; + slave->sii.std_tx_mailbox_size = 0; + slave->sii.mailbox_protocols = 0; + EC_SLAVE_ERR(slave, "Unexpected mailbox offset in SII data.\n"); + } + if (slave->sii_nwords == EC_FIRST_SII_CATEGORY_OFFSET) { // sii does not contain category data fsm->state = ec_fsm_slave_scan_state_end; @@ -979,7 +998,7 @@ void ec_fsm_slave_scan_state_preop( return; } - ec_fsm_slave_scan_enter_pdos(fsm); + ec_fsm_slave_scan_enter_clear_mailbox(fsm); } /*****************************************************************************/ @@ -1050,6 +1069,51 @@ void ec_fsm_slave_scan_state_sync( // allocate memory for mailbox response data for supported mailbox protocols ec_mbox_prot_data_prealloc(slave, slave->sii.mailbox_protocols, slave->configured_tx_mailbox_size); + ec_fsm_slave_scan_enter_clear_mailbox(fsm); +} + +/*****************************************************************************/ + +/** Enter slave scan state: Clear Mailbox. + */ +void ec_fsm_slave_scan_enter_clear_mailbox( + ec_fsm_slave_scan_t *fsm /**< slave state machine */ + ) +{ + ec_slave_t *slave = fsm->slave; + + // If there is some old data in the slave's mailbox, read it out and + // discard it. We don't need to check the mailbox first, we just ignore + // an error or empty mailbox response. + ec_datagram_t *datagram = fsm->datagram; + ec_slave_mbox_prepare_fetch(fsm->slave, datagram); + fsm->retries = EC_FSM_RETRIES; + fsm->state = ec_fsm_slave_scan_state_mailbox_cleared; + + slave->valid_mbox_data = 0; +} + +/*****************************************************************************/ + +/** Slave scan state: Mailbox cleared. + */ +void ec_fsm_slave_scan_state_mailbox_cleared(ec_fsm_slave_scan_t *fsm /**< slave state machine */) +{ + ec_slave_t *slave = fsm->slave; + ec_datagram_t *datagram = fsm->datagram; + + if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { + ec_slave_mbox_prepare_fetch(fsm->slave, datagram); + return; + } + + if (unlikely(slave->master->debug_level > 0) + && datagram->state == EC_DATAGRAM_RECEIVED + && datagram->working_counter == 1) + EC_SLAVE_INFO(slave, "Cleared old data from the mailbox\n"); + + slave->valid_mbox_data = 1; + ec_fsm_slave_scan_enter_pdos(fsm); } diff --git a/master/fsm_soe.c b/master/fsm_soe.c index e7cbadff..cd904a72 100644 --- a/master/fsm_soe.c +++ b/master/fsm_soe.c @@ -147,35 +147,35 @@ void ec_fsm_soe_transfer( /** Executes the current state of the state machine. * - * \return 1 if the datagram was used, else 0. + * \return 1 if the state machine is still in progress, else 0. */ int ec_fsm_soe_exec( ec_fsm_soe_t *fsm, /**< finite state machine */ ec_datagram_t *datagram /**< Datagram to use. */ ) { - int datagram_used = 0; + if (fsm->state == ec_fsm_soe_end || fsm->state == ec_fsm_soe_error) + return 0; if (fsm->datagram && (fsm->datagram->state == EC_DATAGRAM_INIT || fsm->datagram->state == EC_DATAGRAM_QUEUED || fsm->datagram->state == EC_DATAGRAM_SENT)) { // datagram not received yet - return datagram_used; + if (datagram != fsm->datagram) + datagram->state = EC_DATAGRAM_INVALID; + return 1; } fsm->state(fsm, datagram); - datagram_used = - fsm->state != ec_fsm_soe_end && fsm->state != ec_fsm_soe_error; - - if (datagram_used) { - fsm->datagram = datagram; - } else { + if (fsm->state == ec_fsm_soe_end || fsm->state == ec_fsm_soe_error) { fsm->datagram = NULL; + return 0; } - return datagram_used; + fsm->datagram = datagram; + return 1; } /*****************************************************************************/ 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 5ba49378..282f5452 100644 --- a/master/master.c +++ b/master/master.c @@ -1286,67 +1286,72 @@ void ec_master_receive_datagrams( if (slave->station_address == datagram_slave_addr) { if (slave->configured_tx_mailbox_offset != 0) { if (datagram_offset_addr == slave->configured_tx_mailbox_offset) { - datagram_mbox_prot = EC_READ_U8(cur_data + 5) & 0x0F; - switch (datagram_mbox_prot) { + if (slave->valid_mbox_data) { + datagram_mbox_prot = EC_READ_U8(cur_data + 5) & 0x0F; + switch (datagram_mbox_prot) { #ifdef EC_EOE - case EC_MBOX_TYPE_EOE: - // check EOE type and store in correct handlers mbox data cache - eoe_type = EC_READ_U8(cur_data + 6) & 0x0F; + case EC_MBOX_TYPE_EOE: + // check EOE type and store in correct handlers mbox data cache + eoe_type = EC_READ_U8(cur_data + 6) & 0x0F; - switch (eoe_type) { + switch (eoe_type) { - case EC_EOE_TYPE_FRAME_FRAG: - // EoE Frame Fragment handler - if ((slave->mbox_eoe_frag_data.data) && (data_size <= slave->mbox_eoe_frag_data.data_size)) { - memcpy(slave->mbox_eoe_frag_data.data, cur_data, data_size); - slave->mbox_eoe_frag_data.payload_size = data_size; - } - break; - case EC_EOE_TYPE_INIT_RES: - // EoE Init / Set IP response handler - if ((slave->mbox_eoe_init_data.data) && (data_size <= slave->mbox_eoe_init_data.data_size)) { - memcpy(slave->mbox_eoe_init_data.data, cur_data, data_size); - slave->mbox_eoe_init_data.payload_size = data_size; - } - break; - default: - EC_MASTER_DBG(master, 1, "Unhandled EoE protocol type from slave: %u Protocol: %u, Type: %x\n", - datagram_slave_addr, datagram_mbox_prot, eoe_type); - // copy instead received data into the datagram memory. - memcpy(datagram->data, cur_data, data_size); - break; - } - break; + case EC_EOE_TYPE_FRAME_FRAG: + // EoE Frame Fragment handler + if ((slave->mbox_eoe_frag_data.data) && (data_size <= slave->mbox_eoe_frag_data.data_size)) { + memcpy(slave->mbox_eoe_frag_data.data, cur_data, data_size); + slave->mbox_eoe_frag_data.payload_size = data_size; + } + break; + case EC_EOE_TYPE_INIT_RES: + // EoE Init / Set IP response handler + if ((slave->mbox_eoe_init_data.data) && (data_size <= slave->mbox_eoe_init_data.data_size)) { + memcpy(slave->mbox_eoe_init_data.data, cur_data, data_size); + slave->mbox_eoe_init_data.payload_size = data_size; + } + break; + default: + EC_MASTER_DBG(master, 1, "Unhandled EoE protocol type from slave: %u Protocol: %u, Type: %x\n", + datagram_slave_addr, datagram_mbox_prot, eoe_type); + // copy instead received data into the datagram memory. + memcpy(datagram->data, cur_data, data_size); + break; + } + break; #endif - case EC_MBOX_TYPE_COE: - if ((slave->mbox_coe_data.data) && (data_size <= slave->mbox_coe_data.data_size)) { - memcpy(slave->mbox_coe_data.data, cur_data, data_size); - slave->mbox_coe_data.payload_size = data_size; + case EC_MBOX_TYPE_COE: + if ((slave->mbox_coe_data.data) && (data_size <= slave->mbox_coe_data.data_size)) { + memcpy(slave->mbox_coe_data.data, cur_data, data_size); + slave->mbox_coe_data.payload_size = data_size; + } + break; + case EC_MBOX_TYPE_FOE: + if ((slave->mbox_foe_data.data) && (data_size <= slave->mbox_foe_data.data_size)) { + memcpy(slave->mbox_foe_data.data, cur_data, data_size); + slave->mbox_foe_data.payload_size = data_size; + } + break; + case EC_MBOX_TYPE_SOE: + if ((slave->mbox_soe_data.data) && (data_size <= slave->mbox_soe_data.data_size)) { + memcpy(slave->mbox_soe_data.data, cur_data, data_size); + slave->mbox_soe_data.payload_size = data_size; + } + break; + case EC_MBOX_TYPE_VOE: + if ((slave->mbox_voe_data.data) && (data_size <= slave->mbox_voe_data.data_size)) { + memcpy(slave->mbox_voe_data.data, cur_data, data_size); + slave->mbox_voe_data.payload_size = data_size; + } + break; + default: + EC_MASTER_DBG(master, 1, "Unknown mailbox protocol from slave: %u Protocol: %u\n", datagram_slave_addr, datagram_mbox_prot); + // copy instead received data into the datagram memory. + memcpy(datagram->data, cur_data, data_size); + break; } - break; - case EC_MBOX_TYPE_FOE: - if ((slave->mbox_foe_data.data) && (data_size <= slave->mbox_foe_data.data_size)) { - memcpy(slave->mbox_foe_data.data, cur_data, data_size); - slave->mbox_foe_data.payload_size = data_size; - } - break; - case EC_MBOX_TYPE_SOE: - if ((slave->mbox_soe_data.data) && (data_size <= slave->mbox_soe_data.data_size)) { - memcpy(slave->mbox_soe_data.data, cur_data, data_size); - slave->mbox_soe_data.payload_size = data_size; - } - break; - case EC_MBOX_TYPE_VOE: - if ((slave->mbox_voe_data.data) && (data_size <= slave->mbox_voe_data.data_size)) { - memcpy(slave->mbox_voe_data.data, cur_data, data_size); - slave->mbox_voe_data.payload_size = data_size; - } - break; - default: - EC_MASTER_DBG(master, 1, "Unknown mailbox protocol from slave: %u Protocol: %u\n", datagram_slave_addr, datagram_mbox_prot); + } else { // copy instead received data into the datagram memory. memcpy(datagram->data, cur_data, data_size); - break; } } else { // copy instead received data into the datagram memory. @@ -1404,25 +1409,26 @@ void ec_master_receive_datagrams( void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */) { if (unlikely(jiffies - master->stats.output_jiffies >= HZ)) { - master->stats.output_jiffies = jiffies; - - if (master->stats.timeouts) { - EC_MASTER_WARN(master, "%u datagram%s TIMED OUT!\n", - master->stats.timeouts, - master->stats.timeouts == 1 ? "" : "s"); - master->stats.timeouts = 0; - } - if (master->stats.corrupted) { - EC_MASTER_WARN(master, "%u frame%s CORRUPTED!\n", - master->stats.corrupted, - master->stats.corrupted == 1 ? "" : "s"); - master->stats.corrupted = 0; - } - if (master->stats.unmatched) { - EC_MASTER_WARN(master, "%u datagram%s UNMATCHED!\n", - master->stats.unmatched, - master->stats.unmatched == 1 ? "" : "s"); - master->stats.unmatched = 0; + if (!master->scan_busy || (master->debug_level > 0)) { + master->stats.output_jiffies = jiffies; + if (master->stats.timeouts) { + EC_MASTER_WARN(master, "%u datagram%s TIMED OUT!\n", + master->stats.timeouts, + master->stats.timeouts == 1 ? "" : "s"); + master->stats.timeouts = 0; + } + if (master->stats.corrupted) { + EC_MASTER_WARN(master, "%u frame%s CORRUPTED!\n", + master->stats.corrupted, + master->stats.corrupted == 1 ? "" : "s"); + master->stats.corrupted = 0; + } + if (master->stats.unmatched) { + EC_MASTER_WARN(master, "%u datagram%s UNMATCHED!\n", + master->stats.unmatched, + master->stats.unmatched == 1 ? "" : "s"); + master->stats.unmatched = 0; + } } } } @@ -1628,13 +1634,15 @@ void ec_master_exec_slave_fsms( fsm->slave->ring_position); #endif if (ec_fsm_slave_exec(fsm, datagram)) { - // FSM consumed datagram + if (datagram->state != EC_DATAGRAM_INVALID) { + // FSM consumed datagram #if DEBUG_INJECT - EC_MASTER_DBG(master, 1, "FSM consumed datagram %s\n", - datagram->name); + EC_MASTER_DBG(master, 1, "FSM consumed datagram %s\n", + datagram->name); #endif - master->ext_ring_idx_fsm = - (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE; + master->ext_ring_idx_fsm = + (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE; + } } else { // FSM finished @@ -1654,8 +1662,10 @@ void ec_master_exec_slave_fsms( datagram = ec_master_get_external_datagram(master); if (ec_fsm_slave_exec(&master->fsm_slave->fsm, datagram)) { - master->ext_ring_idx_fsm = - (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE; + if (datagram->state != EC_DATAGRAM_INVALID) { + master->ext_ring_idx_fsm = + (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE; + } list_add_tail(&master->fsm_slave->fsm.list, &master->fsm_exec_list); master->fsm_exec_count++; @@ -1895,12 +1905,16 @@ static int ec_master_eoe_thread(void *priv_data) // actual EoE processing sth_to_send = 0; list_for_each_entry(eoe, &master->eoe_handlers, list) { - ec_eoe_run(eoe); - if (eoe->queue_datagram) { - sth_to_send = 1; - } - if (!ec_eoe_is_idle(eoe)) { - all_idle = 0; + if ((eoe->slave->current_state == EC_SLAVE_STATE_PREOP) || + (eoe->slave->current_state == EC_SLAVE_STATE_SAFEOP) || + (eoe->slave->current_state == EC_SLAVE_STATE_OP)) { + ec_eoe_run(eoe); + if (eoe->queue_datagram) { + sth_to_send = 1; + } + if (!ec_eoe_is_idle(eoe)) { + all_idle = 0; + } } } @@ -2418,6 +2432,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 46c73228..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); @@ -176,6 +177,8 @@ void ec_slave_init( ec_mbox_data_init(&slave->mbox_foe_data); ec_mbox_data_init(&slave->mbox_soe_data); ec_mbox_data_init(&slave->mbox_voe_data); + + slave->valid_mbox_data = 0; } @@ -271,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 5eb25276..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. */ @@ -268,6 +269,8 @@ struct ec_slave ec_mbox_data_t mbox_foe_data; /**< Received mailbox data for FoE. */ ec_mbox_data_t mbox_soe_data; /**< Received mailbox data for SoE. */ ec_mbox_data_t mbox_voe_data; /**< Received mailbox data for VoE. */ + + uint8_t valid_mbox_data; /**< Received mailbox data is valid. */ }; /*****************************************************************************/ 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;}