martin@1583: /****************************************************************************** martin@1583: * martin@1583: * $Id$ martin@1583: * martin@1583: * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH martin@1583: * martin@1583: * This file is part of the IgH EtherCAT Master. martin@1583: * martin@1583: * The IgH EtherCAT Master is free software; you can redistribute it and/or martin@1583: * modify it under the terms of the GNU General Public License version 2, as martin@1583: * published by the Free Software Foundation. martin@1583: * martin@1583: * The IgH EtherCAT Master is distributed in the hope that it will be useful, martin@1583: * but WITHOUT ANY WARRANTY; without even the implied warranty of martin@1583: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General martin@1583: * Public License for more details. martin@1583: * martin@1583: * You should have received a copy of the GNU General Public License along martin@1583: * with the IgH EtherCAT Master; if not, write to the Free Software martin@1583: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA martin@1583: * martin@1583: * --- martin@1583: * martin@1583: * The license mentioned above concerns the source code only. Using the martin@1583: * EtherCAT technology and brand is only permitted in compliance with the martin@1583: * industrial property and similar rights of Beckhoff Automation GmbH. martin@1583: * martin@1583: *****************************************************************************/ martin@1583: martin@1583: /** \file martin@1583: * EtherCAT slave (SDO) state machine. martin@1583: */ martin@1583: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: #include "globals.h" martin@1583: #include "master.h" martin@1583: #include "mailbox.h" martin@1583: martin@1583: #include "fsm_slave.h" martin@1583: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: void ec_fsm_slave_state_idle(ec_fsm_slave_t *); martin@1601: void ec_fsm_slave_state_ready(ec_fsm_slave_t *); martin@1597: int ec_fsm_slave_action_process_sdo(ec_fsm_slave_t *); fp@1831: void ec_fsm_slave_state_sdo_request(ec_fsm_slave_t *); martin@1597: int ec_fsm_slave_action_process_foe(ec_fsm_slave_t *); martin@1597: void ec_fsm_slave_state_foe_request(ec_fsm_slave_t *); fp@1831: int ec_fsm_slave_action_process_soe(ec_fsm_slave_t *); fp@1831: void ec_fsm_slave_state_soe_request(ec_fsm_slave_t *); martin@1583: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: /** Constructor. martin@1583: */ martin@1583: void ec_fsm_slave_init( martin@1583: ec_fsm_slave_t *fsm, /**< Slave state machine. */ martin@1583: ec_slave_t *slave, /**< EtherCAT slave. */ martin@1583: ec_datagram_t *datagram /**< Datagram object to use. */ martin@1583: ) martin@1583: { martin@1583: fsm->slave = slave; martin@1583: fsm->datagram = datagram; martin@1583: fsm->datagram->data_size = 0; fp@1804: fp@1921: EC_SLAVE_DBG(slave, 1, "Init FSM.\n"); fp@1804: fp@1804: fsm->state = ec_fsm_slave_state_idle; martin@1583: martin@1583: // init sub-state-machines martin@1583: ec_fsm_coe_init(&fsm->fsm_coe, fsm->datagram); martin@1597: ec_fsm_foe_init(&fsm->fsm_foe, fsm->datagram); fp@1831: ec_fsm_soe_init(&fsm->fsm_soe, fsm->datagram); martin@1583: } martin@1583: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: /** Destructor. martin@1583: */ martin@1583: void ec_fsm_slave_clear( martin@1583: ec_fsm_slave_t *fsm /**< Master state machine. */ martin@1583: ) martin@1583: { martin@1583: // clear sub-state machines martin@1583: ec_fsm_coe_clear(&fsm->fsm_coe); martin@1597: ec_fsm_foe_clear(&fsm->fsm_foe); fp@1831: ec_fsm_soe_clear(&fsm->fsm_soe); martin@1583: } martin@1583: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: /** Executes the current state of the state machine. martin@1583: * martin@1583: * If the state machine's datagram is not sent or received yet, the execution martin@1583: * of the state machine is delayed to the next cycle. ch1010277@2017: * ch1010277@2017: * \return true, if the state machine was executed ch1010277@2017: */ ch1010277@2017: int ec_fsm_slave_exec( martin@1583: ec_fsm_slave_t *fsm /**< Slave state machine. */ martin@1583: ) martin@1583: { martin@1583: if (fsm->datagram->state == EC_DATAGRAM_SENT martin@1583: || fsm->datagram->state == EC_DATAGRAM_QUEUED) { martin@1583: // datagram was not sent or received yet. ch1010277@2017: return 0; martin@1583: } martin@1583: martin@1583: fsm->state(fsm); ch1010277@2017: return 1; martin@1583: } martin@1583: martin@1601: /*****************************************************************************/ martin@1601: martin@1601: /** Sets the current state of the state machine to READY martin@1601: */ martin@1601: void ec_fsm_slave_ready( fp@1804: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1804: ) fp@1804: { fp@1804: if (fsm->state == ec_fsm_slave_state_idle) { fp@1921: EC_SLAVE_DBG(fsm->slave, 1, "Ready for requests.\n"); fp@1804: fsm->state = ec_fsm_slave_state_ready; fp@1804: } martin@1601: } martin@1601: martin@1583: /****************************************************************************** martin@1583: * Slave state machine martin@1583: *****************************************************************************/ martin@1583: martin@1583: /** Slave state: IDLE. martin@1583: */ martin@1583: void ec_fsm_slave_state_idle( fp@1804: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1804: ) fp@1804: { fp@1804: // do nothing martin@1601: } martin@1601: martin@1601: martin@1601: /*****************************************************************************/ martin@1601: martin@1601: /** Slave state: READY. martin@1601: */ martin@1601: void ec_fsm_slave_state_ready( fp@1804: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1804: ) fp@1804: { fp@1804: // Check for pending external SDO requests fp@1804: if (ec_fsm_slave_action_process_sdo(fsm)) fp@1804: return; fp@1804: fp@1831: // Check for pending FoE requests fp@1804: if (ec_fsm_slave_action_process_foe(fsm)) fp@1804: return; fp@1831: fp@1831: // Check for pending SoE requests fp@1831: if (ec_fsm_slave_action_process_soe(fsm)) fp@1831: return; fp@1804: } martin@1597: martin@1597: /*****************************************************************************/ martin@1597: martin@1597: /** Check for pending SDO requests and process one. martin@1597: * martin@1597: * \return non-zero, if an SDO request is processed. martin@1597: */ martin@1597: int ec_fsm_slave_action_process_sdo( martin@1597: ec_fsm_slave_t *fsm /**< Slave state machine. */ martin@1597: ) martin@1597: { martin@1583: ec_slave_t *slave = fsm->slave; martin@1583: ec_master_sdo_request_t *request, *next; martin@1583: martin@1597: // search the first external request to be processed martin@1596: list_for_each_entry_safe(request, next, &slave->slave_sdo_requests, list) { martin@1598: martin@1604: list_del_init(&request->list); // dequeue fp@1804: if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) { ch1010277@2027: EC_SLAVE_WARN(slave, "Aborting SDO request %p," ch1010277@2027: " slave has error flag set.\n",request); fp@1804: request->req.state = EC_INT_REQUEST_FAILURE; ch1010277@2027: kref_put(&request->refcount,ec_master_sdo_request_release); fp@1804: wake_up(&slave->sdo_queue); fp@1804: fsm->sdo_request = NULL; fp@1804: fsm->state = ec_fsm_slave_state_idle; fp@1804: return 0; fp@1804: } fp@1804: fp@1804: if (slave->current_state == EC_SLAVE_STATE_INIT) { ch1010277@2027: EC_SLAVE_WARN(slave, "Aborting SDO request %p, slave is in INIT.\n",request); fp@1804: request->req.state = EC_INT_REQUEST_FAILURE; ch1010277@2027: kref_put(&request->refcount,ec_master_sdo_request_release); fp@1804: wake_up(&slave->sdo_queue); fp@1804: fsm->sdo_request = NULL; fp@1804: fsm->state = ec_fsm_slave_state_idle; fp@1804: return 0; fp@1804: } fp@1804: fp@1804: request->req.state = EC_INT_REQUEST_BUSY; martin@1583: martin@1583: // Found pending SDO request. Execute it! ch1010277@2027: EC_SLAVE_DBG(slave, 1, "Processing SDO request %p...\n",request); martin@1583: martin@1583: // Start SDO transfer ch1010277@2027: fsm->sdo_request = request; martin@1583: fsm->state = ec_fsm_slave_state_sdo_request; martin@1583: ec_fsm_coe_transfer(&fsm->fsm_coe, slave, &request->req); martin@1583: ec_fsm_coe_exec(&fsm->fsm_coe); // execute immediately ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master,fsm->datagram); martin@1597: return 1; martin@1597: } martin@1597: return 0; martin@1597: } martin@1597: martin@1583: /*****************************************************************************/ martin@1583: martin@1583: /** Slave state: SDO_REQUEST. martin@1583: */ martin@1583: void ec_fsm_slave_state_sdo_request( martin@1583: ec_fsm_slave_t *fsm /**< Slave state machine. */ martin@1583: ) martin@1583: { martin@1583: ec_slave_t *slave = fsm->slave; ch1010277@2027: ec_master_sdo_request_t *request = fsm->sdo_request; martin@1583: martin@1583: if (ec_fsm_coe_exec(&fsm->fsm_coe)) martin@1583: { ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master,fsm->datagram); martin@1583: return; martin@1583: } martin@1583: if (!ec_fsm_coe_success(&fsm->fsm_coe)) { ch1010277@2027: EC_SLAVE_ERR(slave, "Failed to process SDO request %p.\n",request); ch1010277@2027: request->req.state = EC_INT_REQUEST_FAILURE; ch1010277@2027: kref_put(&request->refcount,ec_master_sdo_request_release); fp@1804: wake_up(&slave->sdo_queue); fp@1804: fsm->sdo_request = NULL; fp@1804: fsm->state = ec_fsm_slave_state_idle; martin@1583: return; martin@1583: } martin@1583: ch1010277@2027: EC_SLAVE_DBG(slave, 1, "Finished SDO request %p.\n",request); martin@1597: martin@1583: // SDO request finished ch1010277@2027: request->req.state = EC_INT_REQUEST_SUCCESS; ch1010277@2027: kref_put(&request->refcount,ec_master_sdo_request_release); martin@1596: wake_up(&slave->sdo_queue); martin@1583: martin@1597: fsm->sdo_request = NULL; fp@1804: fsm->state = ec_fsm_slave_state_ready; fp@1804: } martin@1597: martin@1597: /*****************************************************************************/ martin@1597: fp@1831: /** Check for pending FOE requests and process one. fp@1831: * fp@1831: * \return non-zero, if an FOE request is processed. fp@1831: */ fp@1831: int ec_fsm_slave_action_process_foe( fp@1831: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1831: ) fp@1831: { fp@1831: ec_slave_t *slave = fsm->slave; fp@1831: ec_master_foe_request_t *request, *next; fp@1831: fp@1831: // search the first request to be processed fp@1831: list_for_each_entry_safe(request, next, &slave->foe_requests, list) { fp@1831: if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) { fp@1921: EC_SLAVE_WARN(slave, "Aborting FOE request," fp@1921: " slave has error flag set.\n"); fp@1831: request->req.state = EC_INT_REQUEST_FAILURE; ch1010277@2026: wake_up(&slave->foe_queue); fp@1831: fsm->sdo_request = NULL; fp@1831: fsm->state = ec_fsm_slave_state_idle; fp@1831: return 0; fp@1831: } fp@1831: list_del_init(&request->list); // dequeue fp@1831: request->req.state = EC_INT_REQUEST_BUSY; fp@1831: fp@1921: EC_SLAVE_DBG(slave, 1, "Processing FoE request.\n"); fp@1831: fp@1831: fsm->foe_request = &request->req; fp@1831: fsm->state = ec_fsm_slave_state_foe_request; fp@1831: ec_fsm_foe_transfer(&fsm->fsm_foe, slave, &request->req); fp@1831: ec_fsm_foe_exec(&fsm->fsm_foe); ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master,fsm->datagram); fp@1831: return 1; fp@1831: } fp@1831: return 0; fp@1831: } fp@1831: fp@1831: /*****************************************************************************/ fp@1831: martin@1597: /** Slave state: FOE REQUEST. martin@1597: */ martin@1597: void ec_fsm_slave_state_foe_request( martin@1597: ec_fsm_slave_t *fsm /**< Slave state machine. */ martin@1597: ) martin@1597: { martin@1597: ec_slave_t *slave = fsm->slave; martin@1597: ec_foe_request_t *request = fsm->foe_request; martin@1597: martin@1597: if (ec_fsm_foe_exec(&fsm->fsm_foe)) martin@1597: { ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master,fsm->datagram); martin@1597: return; martin@1597: } martin@1597: martin@1597: if (!ec_fsm_foe_success(&fsm->fsm_foe)) { fp@1921: EC_SLAVE_ERR(slave, "Failed to handle FoE request.\n"); martin@1597: request->state = EC_INT_REQUEST_FAILURE; martin@1597: wake_up(&slave->foe_queue); martin@1597: fsm->foe_request = NULL; martin@1597: fsm->state = ec_fsm_slave_state_idle; martin@1597: return; martin@1597: } martin@1597: martin@1597: // finished transferring FoE fp@1930: EC_SLAVE_DBG(slave, 1, "Successfully transferred %zu bytes of FoE" fp@1921: " data.\n", request->data_size); martin@1597: martin@1597: request->state = EC_INT_REQUEST_SUCCESS; martin@1597: wake_up(&slave->foe_queue); martin@1597: martin@1597: fsm->foe_request = NULL; fp@1804: fsm->state = ec_fsm_slave_state_ready; fp@1804: } fp@1804: fp@1804: /*****************************************************************************/ fp@1831: fp@1831: /** Check for pending SoE requests and process one. fp@1831: * fp@1831: * \return non-zero, if a request is processed. fp@1831: */ fp@1831: int ec_fsm_slave_action_process_soe( fp@1831: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1831: ) fp@1831: { fp@1831: ec_slave_t *slave = fsm->slave; fp@1831: ec_master_soe_request_t *req, *next; fp@1831: fp@1831: // search the first request to be processed fp@1831: list_for_each_entry_safe(req, next, &slave->soe_requests, list) { fp@1831: fp@1831: list_del_init(&req->list); // dequeue fp@1831: if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) { fp@1921: EC_SLAVE_WARN(slave, "Aborting SoE request," fp@1921: " slave has error flag set.\n"); fp@1831: req->req.state = EC_INT_REQUEST_FAILURE; fp@1831: wake_up(&slave->soe_queue); fp@1831: fsm->state = ec_fsm_slave_state_idle; fp@1831: return 0; fp@1831: } fp@1831: fp@1831: if (slave->current_state == EC_SLAVE_STATE_INIT) { fp@1921: EC_SLAVE_WARN(slave, "Aborting SoE request, slave is in INIT.\n"); fp@1831: req->req.state = EC_INT_REQUEST_FAILURE; fp@1831: wake_up(&slave->soe_queue); fp@1831: fsm->state = ec_fsm_slave_state_idle; fp@1831: return 0; fp@1831: } fp@1831: fp@1831: req->req.state = EC_INT_REQUEST_BUSY; fp@1831: fp@1831: // Found pending request. Execute it! fp@1921: EC_SLAVE_DBG(slave, 1, "Processing SoE request...\n"); fp@1831: fp@1831: // Start SoE transfer fp@1831: fsm->soe_request = &req->req; fp@1831: fsm->state = ec_fsm_slave_state_soe_request; fp@1831: ec_fsm_soe_transfer(&fsm->fsm_soe, slave, &req->req); fp@1831: ec_fsm_soe_exec(&fsm->fsm_soe); // execute immediately ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master, fsm->datagram); fp@1831: return 1; fp@1831: } fp@1831: return 0; fp@1831: } fp@1831: fp@1831: /*****************************************************************************/ fp@1831: fp@1831: /** Slave state: SOE_REQUEST. fp@1831: */ fp@1831: void ec_fsm_slave_state_soe_request( fp@1831: ec_fsm_slave_t *fsm /**< Slave state machine. */ fp@1831: ) fp@1831: { fp@1831: ec_slave_t *slave = fsm->slave; fp@1831: ec_soe_request_t *request = fsm->soe_request; fp@1831: fp@1831: if (ec_fsm_soe_exec(&fsm->fsm_soe)) { ch1010277@2018: ec_master_queue_request_fsm_datagram(fsm->slave->master, fsm->datagram); fp@1831: return; fp@1831: } fp@1831: fp@1831: if (!ec_fsm_soe_success(&fsm->fsm_soe)) { fp@1921: EC_SLAVE_ERR(slave, "Failed to process SoE request.\n"); fp@1831: request->state = EC_INT_REQUEST_FAILURE; fp@1831: wake_up(&slave->soe_queue); fp@1831: fsm->soe_request = NULL; fp@1831: fsm->state = ec_fsm_slave_state_idle; fp@1831: return; fp@1831: } fp@1831: fp@1921: EC_SLAVE_DBG(slave, 1, "Finished SoE request.\n"); fp@1831: fp@1831: // SoE request finished fp@1831: request->state = EC_INT_REQUEST_SUCCESS; fp@1831: wake_up(&slave->soe_queue); fp@1831: fp@1831: fsm->soe_request = NULL; fp@1831: fsm->state = ec_fsm_slave_state_ready; fp@1831: } fp@1831: fp@1831: /*****************************************************************************/