fp@434: /****************************************************************************** fp@434: * fp@434: * $Id$ fp@434: * fp@1326: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH fp@434: * fp@434: * This file is part of the IgH EtherCAT Master. fp@434: * fp@1326: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@1326: * modify it under the terms of the GNU General Public License version 2, as fp@1326: * published by the Free Software Foundation. fp@1326: * fp@1326: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@1326: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@1326: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@1326: * Public License for more details. fp@1326: * fp@1326: * You should have received a copy of the GNU General Public License along fp@1326: * with the IgH EtherCAT Master; if not, write to the Free Software fp@434: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@434: * fp@1363: * --- fp@1363: * fp@1363: * The license mentioned above concerns the source code only. Using the fp@1363: * EtherCAT technology and brand is only permitted in compliance with the fp@1363: * industrial property and similar rights of Beckhoff Automation GmbH. fp@434: * fp@434: *****************************************************************************/ fp@434: fp@434: /** fp@434: \file fp@434: EtherCAT state change FSM. fp@434: */ fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: #include "globals.h" fp@434: #include "master.h" fp@434: #include "fsm_change.h" fp@434: fp@434: /*****************************************************************************/ fp@434: fp@1445: /** Timeout while waiting for AL state change [s]. fp@1445: */ fp@1967: #define EC_AL_STATE_CHANGE_TIMEOUT 5 fp@1445: fp@1445: /*****************************************************************************/ fp@1445: fp@454: void ec_fsm_change_state_start(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_check(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_status(ec_fsm_change_t *); fp@2303: void ec_fsm_change_state_start_code(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_code(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_ack(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_check_ack(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_end(ec_fsm_change_t *); fp@454: void ec_fsm_change_state_error(ec_fsm_change_t *); fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Constructor. fp@434: */ fp@434: fp@434: void ec_fsm_change_init(ec_fsm_change_t *fsm, /**< finite state machine */ fp@434: ec_datagram_t *datagram /**< datagram */ fp@434: ) fp@434: { fp@434: fsm->state = NULL; fp@434: fsm->datagram = datagram; fp@738: fsm->spontaneous_change = 0; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Destructor. fp@434: */ fp@434: fp@434: void ec_fsm_change_clear(ec_fsm_change_t *fsm /**< finite state machine */) fp@434: { fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@454: Starts the change state machine. fp@454: */ fp@454: fp@454: void ec_fsm_change_start(ec_fsm_change_t *fsm, /**< finite state machine */ fp@454: ec_slave_t *slave, /**< EtherCAT slave */ fp@454: ec_slave_state_t state /**< requested state */ fp@454: ) fp@454: { fp@454: fsm->mode = EC_FSM_CHANGE_MODE_FULL; fp@434: fsm->slave = slave; fp@434: fsm->requested_state = state; fp@454: fsm->state = ec_fsm_change_state_start; fp@454: } fp@454: fp@454: /*****************************************************************************/ fp@454: fp@454: /** fp@454: Starts the change state machine to only acknowlegde a slave's state. fp@454: */ fp@454: fp@454: void ec_fsm_change_ack(ec_fsm_change_t *fsm, /**< finite state machine */ fp@454: ec_slave_t *slave /**< EtherCAT slave */ fp@454: ) fp@454: { fp@454: fsm->mode = EC_FSM_CHANGE_MODE_ACK_ONLY; fp@454: fsm->slave = slave; fp@454: fsm->requested_state = EC_SLAVE_STATE_UNKNOWN; fp@2303: fsm->state = ec_fsm_change_state_start_code; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Executes the current state of the state machine. fp@435: \return false, if the state machine has terminated fp@435: */ fp@435: fp@435: int ec_fsm_change_exec(ec_fsm_change_t *fsm /**< finite state machine */) fp@434: { fp@434: fsm->state(fsm); fp@435: fp@454: return fsm->state != ec_fsm_change_state_end fp@454: && fsm->state != ec_fsm_change_state_error; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Returns, if the state machine terminated with success. fp@434: \return non-zero if successful. fp@434: */ fp@434: fp@434: int ec_fsm_change_success(ec_fsm_change_t *fsm /**< Finite state machine */) fp@434: { fp@454: return fsm->state == ec_fsm_change_state_end; fp@434: } fp@434: fp@434: /****************************************************************************** fp@434: * state change state machine fp@434: *****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: START. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_start(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@434: { fp@434: ec_datagram_t *datagram = fsm->datagram; fp@434: ec_slave_t *slave = fsm->slave; fp@434: fp@434: fsm->take_time = 1; fp@454: fsm->old_state = fsm->slave->current_state; fp@434: fp@434: // write new state to slave fp@815: ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2); fp@434: EC_WRITE_U16(datagram->data, fsm->requested_state); fp@505: fsm->retries = EC_FSM_RETRIES; fp@454: fsm->state = ec_fsm_change_state_check; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: CHECK. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_check(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@434: { fp@434: ec_datagram_t *datagram = fsm->datagram; fp@434: ec_slave_t *slave = fsm->slave; fp@434: fp@637: if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) fp@637: return; fp@505: fp@434: if (datagram->state != EC_DATAGRAM_RECEIVED) { fp@454: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to receive state datagram: "); fp@1822: ec_datagram_print_state(datagram); fp@434: return; fp@434: } fp@434: fp@434: if (fsm->take_time) { fp@434: fsm->take_time = 0; fp@434: fsm->jiffies_start = datagram->jiffies_sent; fp@434: } fp@434: fp@713: if (datagram->working_counter == 0) { fp@434: if (datagram->jiffies_received - fsm->jiffies_start >= 3 * HZ) { fp@438: char state_str[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(fsm->requested_state, state_str, 0); fp@454: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str); fp@713: ec_datagram_print_wc_error(datagram); fp@434: return; fp@434: } fp@434: fp@434: // repeat writing new state to slave fp@815: ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2); fp@434: EC_WRITE_U16(datagram->data, fsm->requested_state); fp@505: fsm->retries = EC_FSM_RETRIES; fp@434: return; fp@434: } fp@434: fp@713: if (unlikely(datagram->working_counter > 1)) { fp@713: char state_str[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(fsm->requested_state, state_str, 0); fp@713: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str); fp@713: ec_datagram_print_wc_error(datagram); fp@713: return; fp@713: } fp@713: fp@434: fsm->take_time = 1; fp@434: fp@434: // read AL status from slave fp@815: ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2); fp@1225: ec_datagram_zero(datagram); fp@505: fsm->retries = EC_FSM_RETRIES; fp@738: fsm->spontaneous_change = 0; fp@454: fsm->state = ec_fsm_change_state_status; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: STATUS. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_status(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@434: { fp@434: ec_datagram_t *datagram = fsm->datagram; fp@434: ec_slave_t *slave = fsm->slave; fp@434: fp@637: if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) fp@637: return; fp@505: fp@505: if (datagram->state != EC_DATAGRAM_RECEIVED) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to receive state checking datagram: "); fp@1822: ec_datagram_print_state(datagram); fp@505: return; fp@505: } fp@505: fp@505: if (datagram->working_counter != 1) { fp@469: char req_state[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(fsm->requested_state, req_state, 0); fp@469: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to check state %s: ", req_state); fp@713: ec_datagram_print_wc_error(datagram); fp@434: return; fp@434: } fp@434: fp@434: if (fsm->take_time) { fp@434: fsm->take_time = 0; fp@434: fsm->jiffies_start = datagram->jiffies_sent; fp@434: } fp@434: fp@434: slave->current_state = EC_READ_U8(datagram->data); fp@434: fp@434: if (slave->current_state == fsm->requested_state) { fp@434: // state has been set successfully fp@454: fsm->state = ec_fsm_change_state_end; fp@454: return; fp@454: } fp@454: fp@454: if (slave->current_state != fsm->old_state) { // state changed fp@438: char req_state[EC_STATE_STRING_SIZE], cur_state[EC_STATE_STRING_SIZE]; fp@454: fp@1337: ec_state_string(slave->current_state, cur_state, 0); fp@469: fp@469: if ((slave->current_state & 0x0F) != (fsm->old_state & 0x0F)) { fp@469: // Slave spontaneously changed its state just before the new state fp@469: // was written. Accept current state as old state and wait for fp@469: // state change fp@738: fsm->spontaneous_change = 1; fp@469: fsm->old_state = slave->current_state; fp@1921: EC_SLAVE_WARN(slave, "Changed to %s in the meantime.\n", fp@1921: cur_state); fp@738: goto check_again; fp@469: } fp@469: fp@469: // state change error fp@469: fp@454: slave->error_flag = 1; fp@1337: ec_state_string(fsm->requested_state, req_state, 0); fp@469: fp@1921: EC_SLAVE_ERR(slave, "Failed to set %s state, slave refused state" fp@1921: " change (%s).\n", req_state, cur_state); fp@2303: fp@2303: ec_fsm_change_state_start_code(fsm); fp@454: return; fp@454: } fp@454: fp@454: // still old state fp@434: fp@1445: if (datagram->jiffies_received - fsm->jiffies_start >= fp@1445: EC_AL_STATE_CHANGE_TIMEOUT * HZ) { fp@434: // timeout while checking fp@438: char state_str[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(fsm->requested_state, state_str, 0); fp@454: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Timeout while setting state %s.\n", state_str); fp@738: return; fp@738: } fp@738: fp@738: check_again: fp@469: // no timeout yet. check again fp@815: ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2); fp@1225: ec_datagram_zero(datagram); fp@505: fsm->retries = EC_FSM_RETRIES; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@2303: /** Enter reading AL status code. fp@2303: */ fp@2303: void ec_fsm_change_state_start_code( fp@2303: ec_fsm_change_t *fsm /**< finite state machine */ fp@2303: ) fp@2303: { fp@2303: ec_slave_t *slave = fsm->slave; fp@2303: ec_datagram_t *datagram = fsm->datagram; fp@2303: fp@2303: // fetch AL status error code fp@2303: ec_datagram_fprd(datagram, slave->station_address, 0x0134, 2); fp@2303: ec_datagram_zero(datagram); fp@2303: fsm->retries = EC_FSM_RETRIES; fp@2303: fsm->state = ec_fsm_change_state_code; fp@2303: } fp@2303: fp@2303: /*****************************************************************************/ fp@2303: fp@434: /** fp@434: Application layer status messages. fp@434: */ fp@434: fp@434: const ec_code_msg_t al_status_messages[] = { fp@1886: {0x0000, "No error"}, fp@434: {0x0001, "Unspecified error"}, fp@1886: {0x0002, "No Memory"}, fp@1337: {0x0011, "Invalid requested state change"}, fp@434: {0x0012, "Unknown requested state"}, fp@434: {0x0013, "Bootstrap not supported"}, fp@434: {0x0014, "No valid firmware"}, fp@434: {0x0015, "Invalid mailbox configuration"}, fp@434: {0x0016, "Invalid mailbox configuration"}, fp@434: {0x0017, "Invalid sync manager configuration"}, fp@434: {0x0018, "No valid inputs available"}, fp@434: {0x0019, "No valid outputs"}, fp@1886: {0x001A, "Synchronization error"}, fp@434: {0x001B, "Sync manager watchdog"}, fp@434: {0x001C, "Invalid sync manager types"}, fp@434: {0x001D, "Invalid output configuration"}, fp@434: {0x001E, "Invalid input configuration"}, fp@434: {0x001F, "Invalid watchdog configuration"}, fp@434: {0x0020, "Slave needs cold start"}, fp@434: {0x0021, "Slave needs INIT"}, fp@434: {0x0022, "Slave needs PREOP"}, fp@813: {0x0023, "Slave needs SAFEOP"}, fp@1886: {0x0024, "Invalid Input Mapping"}, fp@1886: {0x0025, "Invalid Output Mapping"}, fp@1886: {0x0026, "Inconsistent Settings"}, fp@1886: {0x0027, "Freerun not supported"}, fp@1886: {0x0028, "Synchronization not supported"}, fp@1886: {0x0029, "Freerun needs 3 Buffer Mode"}, fp@1886: {0x002A, "Background Watchdog"}, fp@1886: {0x002B, "No Valid Inputs and Outputs"}, fp@1886: {0x002C, "Fatal Sync Error"}, fp@1886: {0x002D, "No Sync Error"}, fp@434: {0x0030, "Invalid DC SYNCH configuration"}, fp@434: {0x0031, "Invalid DC latch configuration"}, fp@434: {0x0032, "PLL error"}, fp@1886: {0x0033, "DC Sync IO Error"}, fp@1886: {0x0034, "DC Sync Timeout Error"}, fp@1886: {0x0035, "DC Invalid Sync Cycle Time"}, fp@1886: {0x0036, "DC Sync0 Cycle Time"}, fp@1886: {0x0037, "DC Sync1 Cycle Time"}, fp@1886: {0x0041, "MBX_AOE"}, fp@1886: {0x0042, "MBX_EOE"}, fp@1886: {0x0043, "MBX_COE"}, fp@1886: {0x0044, "MBX_FOE"}, fp@1886: {0x0045, "MBX_SOE"}, fp@1886: {0x004F, "MBX_VOE"}, fp@1886: {0x0050, "EEPROM No Access"}, fp@1886: {0x0051, "EEPROM Error"}, fp@1886: {0x0060, "Slave Restarted Locally"}, fp@1886: {0xffff} fp@434: }; fp@434: fp@1886: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: CODE. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_code(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@454: { fp@454: ec_datagram_t *datagram = fsm->datagram; fp@434: uint32_t code; fp@434: const ec_code_msg_t *al_msg; fp@434: fp@637: if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) fp@637: return; fp@505: fp@505: if (datagram->state != EC_DATAGRAM_RECEIVED) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(fsm->slave, "Failed to receive" fp@1921: " AL status code datagram: "); fp@1822: ec_datagram_print_state(datagram); fp@505: return; fp@505: } fp@505: fp@505: if (datagram->working_counter != 1) { fp@1921: EC_SLAVE_WARN(fsm->slave, "Reception of AL status code" fp@1921: " datagram failed: "); fp@713: ec_datagram_print_wc_error(datagram); fp@798: } else { fp@798: code = EC_READ_U16(datagram->data); fp@1886: for (al_msg = al_status_messages; al_msg->code != 0xffff; al_msg++) { fp@798: if (al_msg->code != code) continue; fp@1921: EC_SLAVE_ERR(fsm->slave, "AL status message 0x%04X: \"%s\".\n", fp@798: al_msg->code, al_msg->message); fp@798: break; fp@434: } fp@798: if (!al_msg->code) fp@1921: EC_SLAVE_ERR(fsm->slave, "Unknown AL status code 0x%04X.\n", fp@1921: code); fp@434: } fp@434: fp@434: // acknowledge "old" slave state fp@2303: ec_datagram_fpwr(datagram, fsm->slave->station_address, 0x0120, 2); fp@2303: EC_WRITE_U16(datagram->data, fsm->slave->current_state); fp@505: fsm->retries = EC_FSM_RETRIES; fp@454: fsm->state = ec_fsm_change_state_ack; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: ACK. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_ack(ec_fsm_change_t *fsm /**< finite state machine */) fp@434: { fp@434: ec_datagram_t *datagram = fsm->datagram; fp@434: ec_slave_t *slave = fsm->slave; fp@434: fp@637: if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) fp@637: return; fp@505: fp@505: if (datagram->state != EC_DATAGRAM_RECEIVED) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to receive state ack datagram: "); fp@1822: ec_datagram_print_state(datagram); fp@505: return; fp@505: } fp@505: fp@505: if (datagram->working_counter != 1) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Reception of state ack datagram failed: "); fp@713: ec_datagram_print_wc_error(datagram); fp@434: return; fp@434: } fp@434: fp@434: fsm->take_time = 1; fp@434: fp@434: // read new AL status fp@815: ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2); fp@1225: ec_datagram_zero(datagram); fp@505: fsm->retries = EC_FSM_RETRIES; fp@454: fsm->state = ec_fsm_change_state_check_ack; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: Change state: CHECK ACK. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_check_ack(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@434: { fp@434: ec_datagram_t *datagram = fsm->datagram; fp@434: ec_slave_t *slave = fsm->slave; fp@434: fp@637: if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) fp@637: return; fp@505: fp@505: if (datagram->state != EC_DATAGRAM_RECEIVED) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Failed to receive state ack check datagram: "); fp@1822: ec_datagram_print_state(datagram); fp@505: return; fp@505: } fp@505: fp@505: if (datagram->working_counter != 1) { fp@505: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Reception of state ack check datagram failed: "); fp@713: ec_datagram_print_wc_error(datagram); fp@434: return; fp@434: } fp@434: fp@434: if (fsm->take_time) { fp@434: fsm->take_time = 0; fp@434: fsm->jiffies_start = datagram->jiffies_sent; fp@434: } fp@434: fp@434: slave->current_state = EC_READ_U8(datagram->data); fp@434: fp@434: if (!(slave->current_state & EC_SLAVE_STATE_ACK_ERR)) { fp@438: char state_str[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(slave->current_state, state_str, 0); fp@454: if (fsm->mode == EC_FSM_CHANGE_MODE_FULL) { fp@454: fsm->state = ec_fsm_change_state_error; fp@454: } fp@454: else { // EC_FSM_CHANGE_MODE_ACK_ONLY fp@454: fsm->state = ec_fsm_change_state_end; fp@454: } fp@1921: EC_SLAVE_INFO(slave, "Acknowledged state %s.\n", state_str); fp@434: return; fp@434: } fp@434: fp@1445: if (datagram->jiffies_received - fsm->jiffies_start >= fp@1445: EC_AL_STATE_CHANGE_TIMEOUT * HZ) { fp@443: // timeout while checking fp@438: char state_str[EC_STATE_STRING_SIZE]; fp@1337: ec_state_string(slave->current_state, state_str, 0); fp@454: fsm->state = ec_fsm_change_state_error; fp@1921: EC_SLAVE_ERR(slave, "Timeout while acknowledging state %s.\n", fp@1921: state_str); fp@434: return; fp@434: } fp@434: fp@434: // reread new AL status fp@815: ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2); fp@1225: ec_datagram_zero(datagram); fp@505: fsm->retries = EC_FSM_RETRIES; fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: State: ERROR. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_error(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@434: { fp@434: } fp@434: fp@434: /*****************************************************************************/ fp@434: fp@434: /** fp@434: State: END. fp@434: */ fp@434: fp@454: void ec_fsm_change_state_end(ec_fsm_change_t *fsm fp@454: /**< finite state machine */) fp@454: { fp@454: } fp@454: fp@454: /*****************************************************************************/