AL state timeout lookup logic.

This commit is contained in:
Florian Pose
2024-05-22 11:54:36 +02:00
parent 36d036b081
commit b182bef611
4 changed files with 169 additions and 15 deletions

View File

@@ -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().

View File

@@ -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;
}

View File

@@ -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 */

View File

@@ -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