From b182bef61119960e2b0fd7cfca27522a13857097 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Wed, 22 May 2024 11:54:36 +0200 Subject: [PATCH] AL state timeout lookup logic. --- include/ecrt.h | 5 +- master/fsm_change.c | 69 +++++++++++++++++++++------ master/slave_config.c | 106 ++++++++++++++++++++++++++++++++++++++++++ master/slave_config.h | 4 ++ 4 files changed, 169 insertions(+), 15 deletions(-) diff --git a/include/ecrt.h b/include/ecrt.h index 3a6addcf..00e2eb8c 100644 --- a/include/ecrt.h +++ b/include/ecrt.h @@ -2014,8 +2014,9 @@ EC_PUBLIC_API int ecrt_slave_config_eoe_hostname( * * Change the maximum allowed time for a slave to make an application-layer * state transition for the given state transition (for example from PREOP to - * SAFEOP). The default timeout is 10 s. Some transitions of specific slaves - * may take longer. + * SAFEOP). The default values are defined in ETG.2000. + * + * A timeout value of zero ms will restore the default value. * * This method has to be called in non-realtime context before * ecrt_master_activate(). diff --git a/master/fsm_change.c b/master/fsm_change.c index 0dc10d6b..98fae184 100644 --- a/master/fsm_change.c +++ b/master/fsm_change.c @@ -29,17 +29,11 @@ #include "globals.h" #include "master.h" #include "fsm_change.h" +#include "slave_config.h" /****************************************************************************/ -/** Timeout while waiting for AL state change [s]. - * - * ETG2000_S_R_V1i0i15 section 5.3.7.2 mentions 10 s as maximum AL state - * change timeout. - */ -#define EC_AL_STATE_CHANGE_TIMEOUT 10 - -/****************************************************************************/ +unsigned int ec_fsm_change_timeout_ms(const ec_fsm_change_t *); void ec_fsm_change_state_start(ec_fsm_change_t *); void ec_fsm_change_state_check(ec_fsm_change_t *); @@ -78,6 +72,50 @@ void ec_fsm_change_clear(ec_fsm_change_t *fsm /**< finite state machine */) /****************************************************************************/ +/** Get timeout in ms. + * + * For defaults see ETG2000_S_R_V1i0i15 section 5.3.6.2. + */ +unsigned int ec_fsm_change_timeout_ms( + const ec_fsm_change_t *fsm /**< finite state machine */ + ) +{ + ec_slave_state_t from = fsm->old_state; + ec_slave_state_t to = fsm->requested_state; + + /* Search for specified timeout in slave configuration */ + if (fsm->slave->config) { + unsigned int timeout_ms = + ec_slave_config_al_timeout(fsm->slave->config, from, to); + if (timeout_ms) { + return timeout_ms; + } + } + + /* No specific timeout found. Use defaults from spec. */ + + if (from == EC_SLAVE_STATE_INIT && + (to == EC_SLAVE_STATE_PREOP || to == EC_SLAVE_STATE_BOOT)) { + return 3000; // PreopTimeout + } + if ((from == EC_SLAVE_STATE_PREOP && to == EC_SLAVE_STATE_SAFEOP) || + (from == EC_SLAVE_STATE_SAFEOP && to == EC_SLAVE_STATE_OP)) { + return 10000; // SafeopOpTimeout + } + if (to == EC_SLAVE_STATE_INIT || + ((from == EC_SLAVE_STATE_OP || from == EC_SLAVE_STATE_SAFEOP) + && to == EC_SLAVE_STATE_PREOP)) { + return 5000; // BackToInitTimeout + } + if (from == EC_SLAVE_STATE_OP && to == EC_SLAVE_STATE_SAFEOP) { + return 200; // BackToSafeopTimeout + } + + return 10000; // default [ms] +} + +/****************************************************************************/ + /** Starts the change state machine. */ @@ -234,6 +272,7 @@ void ec_fsm_change_state_status(ec_fsm_change_t *fsm { ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; + unsigned int timeout_ms; if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) return; @@ -297,13 +336,15 @@ void ec_fsm_change_state_status(ec_fsm_change_t *fsm // still old state + timeout_ms = ec_fsm_change_timeout_ms(fsm); if (datagram->jiffies_received - fsm->jiffies_start >= - EC_AL_STATE_CHANGE_TIMEOUT * HZ) { + timeout_ms * HZ / 1000) { // timeout while checking char state_str[EC_STATE_STRING_SIZE]; ec_state_string(fsm->requested_state, state_str, 0); fsm->state = ec_fsm_change_state_error; - EC_SLAVE_ERR(slave, "Timeout while setting state %s.\n", state_str); + EC_SLAVE_ERR(slave, "Timeout after %u ms while setting state %s.\n", + timeout_ms, state_str); return; } @@ -492,6 +533,7 @@ void ec_fsm_change_state_check_ack(ec_fsm_change_t *fsm { ec_datagram_t *datagram = fsm->datagram; ec_slave_t *slave = fsm->slave; + unsigned int timeout_ms; if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) return; @@ -530,14 +572,15 @@ void ec_fsm_change_state_check_ack(ec_fsm_change_t *fsm return; } + timeout_ms = ec_fsm_change_timeout_ms(fsm); if (datagram->jiffies_received - fsm->jiffies_start >= - EC_AL_STATE_CHANGE_TIMEOUT * HZ) { + timeout_ms * HZ / 1000) { // timeout while checking char state_str[EC_STATE_STRING_SIZE]; ec_state_string(slave->current_state, state_str, 0); fsm->state = ec_fsm_change_state_error; - EC_SLAVE_ERR(slave, "Timeout while acknowledging state %s.\n", - state_str); + EC_SLAVE_ERR(slave, "Timeout after %u ms while acknowledging" + " state %s.\n", timeout_ms, state_str); return; } diff --git a/master/slave_config.c b/master/slave_config.c index e331aa81..87e81a6c 100644 --- a/master/slave_config.c +++ b/master/slave_config.c @@ -45,6 +45,25 @@ /****************************************************************************/ +// prototypes for private methods +int ec_slave_config_prepare_fmmu(ec_slave_config_t *, ec_domain_t *, uint8_t, + ec_direction_t); +void ec_slave_config_load_default_mapping(const ec_slave_config_t *, + ec_pdo_t *); + +/****************************************************************************/ + +/** EtherCAT application-layer transition timeout. + */ +typedef struct { + struct list_head list; + ec_slave_state_t from; + ec_slave_state_t to; + unsigned int timeout_ms; +} ec_al_timeout_t; + +/****************************************************************************/ + /** Slave configuration constructor. * * See ecrt_master_slave_config() for the usage of the \a alias and \a @@ -89,6 +108,7 @@ void ec_slave_config_init( INIT_LIST_HEAD(&sc->voe_handlers); INIT_LIST_HEAD(&sc->soe_configs); INIT_LIST_HEAD(&sc->flags); + INIT_LIST_HEAD(&sc->al_timeouts); #ifdef EC_EOE ec_eoe_request_init(&sc->eoe_ip_param_request); @@ -113,6 +133,7 @@ void ec_slave_config_clear( ec_reg_request_t *reg, *next_reg; ec_soe_request_t *soe, *next_soe; ec_flag_t *flag, *next_flag; + ec_al_timeout_t *timeout, *next_timeout; ec_slave_config_detach(sc); @@ -169,6 +190,12 @@ void ec_slave_config_clear( kfree(flag); } + // free all AL timeouts + list_for_each_entry_safe(timeout, next_timeout, &sc->al_timeouts, list) { + list_del(&timeout->list); + kfree(timeout); + } + ec_coe_emerg_ring_clear(&sc->emerg_ring); } @@ -625,6 +652,26 @@ ec_flag_t *ec_slave_config_find_flag( return NULL; } +/****************************************************************************/ + +/** Return an AL state timeout. + * + * \return Search result, or 0. + */ +unsigned int ec_slave_config_al_timeout(const ec_slave_config_t *sc, + ec_slave_state_t from, ec_slave_state_t to) +{ + ec_al_timeout_t *timeout; + + list_for_each_entry(timeout, &sc->al_timeouts, list) { + if (timeout->from == from && timeout->to == to) { + return timeout->timeout_ms; + } + } + + return 0; +} + /***************************************************************************** * Application interface ****************************************************************************/ @@ -1504,6 +1551,64 @@ int ecrt_slave_config_eoe_hostname(ec_slave_config_t *sc, /****************************************************************************/ +int ecrt_slave_config_state_timeout(ec_slave_config_t *sc, + ec_al_state_t from, ec_al_state_t to, unsigned int timeout_ms) +{ + ec_al_timeout_t *timeout; + ec_slave_state_t from_state, to_state; + + if (from != EC_AL_STATE_INIT && from != EC_AL_STATE_PREOP && + from != EC_AL_STATE_SAFEOP && from != EC_AL_STATE_OP) { + EC_CONFIG_ERR(sc, "Invalid from state %i.\n", from); + return -EINVAL; + } + if (to != EC_AL_STATE_INIT && to != EC_AL_STATE_PREOP && + to != EC_AL_STATE_SAFEOP && to != EC_AL_STATE_OP) { + EC_CONFIG_ERR(sc, "Invalid to state %i.\n", to); + return -EINVAL; + } + + from_state = (ec_slave_state_t) from; + to_state = (ec_slave_state_t) to; + + /* try to find an already configured timeout. */ + list_for_each_entry(timeout, &sc->al_timeouts, list) { + if (timeout->from == from_state && timeout->to == to_state) { + if (timeout_ms == 0) { + // delete configured value + list_del(&timeout->list); + kfree(timeout); + return 0; + } + timeout->timeout_ms = timeout_ms; + return 0; + } + } + + if (timeout_ms == 0) { + return 0; + } + + /* no timeout found. create one. */ + if (!(timeout = (ec_al_timeout_t *) + kmalloc(sizeof(ec_al_timeout_t), GFP_KERNEL))) { + EC_CONFIG_ERR(sc, "Failed to allocate memory for" + " AL timeout configuration!\n"); + return -ENOMEM; + } + + timeout->from = from_state; + timeout->to = to_state; + timeout->timeout_ms = timeout_ms; + + down(&sc->master->master_sem); + list_add_tail(&timeout->list, &sc->al_timeouts); + up(&sc->master->master_sem); + return 0; +} + +/****************************************************************************/ + /** \cond */ EXPORT_SYMBOL(ecrt_slave_config_sync_manager); @@ -1540,6 +1645,7 @@ EXPORT_SYMBOL(ecrt_slave_config_eoe_default_gateway); EXPORT_SYMBOL(ecrt_slave_config_eoe_dns_address); EXPORT_SYMBOL(ecrt_slave_config_eoe_hostname); #endif +EXPORT_SYMBOL(ecrt_slave_config_state_timeout); /** \endcond */ diff --git a/master/slave_config.h b/master/slave_config.h index 688156fc..124995c2 100644 --- a/master/slave_config.h +++ b/master/slave_config.h @@ -140,6 +140,7 @@ struct ec_slave_config { struct list_head reg_requests; /**< List of register requests. */ struct list_head soe_configs; /**< List of SoE configurations. */ struct list_head flags; /**< List of feature flags. */ + struct list_head al_timeouts; /**< List of specific AL state timeouts. */ #ifdef EC_EOE ec_eoe_request_t eoe_ip_param_request; /**< EoE IP parameters. */ @@ -187,6 +188,9 @@ ec_voe_handler_t *ecrt_slave_config_create_voe_handler_err( ec_reg_request_t *ecrt_slave_config_create_reg_request_err( ec_slave_config_t *, size_t); +unsigned int ec_slave_config_al_timeout(const ec_slave_config_t *, + ec_slave_state_t, ec_slave_state_t); + /****************************************************************************/ #endif