fp@2597: /****************************************************************************** fp@2597: * fp@2597: * $Id$ fp@2597: * fp@2597: * Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH fp@2597: * fp@2597: * This file is part of the IgH EtherCAT Master. fp@2597: * fp@2597: * The IgH EtherCAT Master is free software; you can redistribute it and/or fp@2597: * modify it under the terms of the GNU General Public License version 2, as fp@2597: * published by the Free Software Foundation. fp@2597: * fp@2597: * The IgH EtherCAT Master is distributed in the hope that it will be useful, fp@2597: * but WITHOUT ANY WARRANTY; without even the implied warranty of fp@2597: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General fp@2597: * Public License for more details. fp@2597: * fp@2597: * You should have received a copy of the GNU General Public License along fp@2597: * with the IgH EtherCAT Master; if not, write to the Free Software fp@2597: * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA fp@2597: * fp@2597: * --- fp@2597: * fp@2597: * The license mentioned above concerns the source code only. Using the fp@2597: * EtherCAT technology and brand is only permitted in compliance with the fp@2597: * industrial property and similar rights of Beckhoff Automation GmbH. fp@2597: * fp@2597: *****************************************************************************/ fp@2597: fp@2597: /** fp@2597: \file fp@2597: EtherCAT EoE state machines. fp@2597: */ fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: #include "globals.h" fp@2597: #include "master.h" fp@2597: #include "mailbox.h" fp@2597: #include "fsm_eoe.h" fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Maximum time to wait for a set IP parameter response. fp@2597: */ fp@2597: #define EC_EOE_RESPONSE_TIMEOUT 3000 // [ms] fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: void ec_fsm_eoe_set_ip_start(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: void ec_fsm_eoe_set_ip_request(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: void ec_fsm_eoe_set_ip_check(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: void ec_fsm_eoe_set_ip_response(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: fp@2597: void ec_fsm_eoe_end(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: void ec_fsm_eoe_error(ec_fsm_eoe_t *, ec_datagram_t *); fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Constructor. fp@2597: */ fp@2597: void ec_fsm_eoe_init( fp@2597: ec_fsm_eoe_t *fsm /**< finite state machine */ fp@2597: ) fp@2597: { fp@2597: fsm->slave = NULL; fp@2597: fsm->retries = 0; fp@2597: fsm->state = NULL; fp@2597: fsm->datagram = NULL; fp@2597: fsm->jiffies_start = 0; fp@2597: fsm->request = NULL; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Destructor. fp@2597: */ fp@2597: void ec_fsm_eoe_clear( fp@2597: ec_fsm_eoe_t *fsm /**< finite state machine */ fp@2597: ) fp@2597: { fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Starts to set the EoE IP partameters of a slave. fp@2597: */ fp@2597: void ec_fsm_eoe_set_ip_param( fp@2597: ec_fsm_eoe_t *fsm, /**< State machine. */ fp@2597: ec_slave_t *slave, /**< EtherCAT slave. */ fp@2597: ec_eoe_request_t *request /**< EoE request. */ fp@2597: ) fp@2597: { fp@2597: fsm->slave = slave; fp@2597: fsm->request = request; fp@2597: fsm->state = ec_fsm_eoe_set_ip_start; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Executes the current state of the state machine. fp@2597: * fp@2597: * \return 1 if the datagram was used, else 0. fp@2597: */ fp@2597: int ec_fsm_eoe_exec( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: int datagram_used = 0; fp@2597: fp@2597: if (fsm->datagram && fp@2597: (fsm->datagram->state == EC_DATAGRAM_INIT || fp@2597: fsm->datagram->state == EC_DATAGRAM_QUEUED || fp@2597: fsm->datagram->state == EC_DATAGRAM_SENT)) { fp@2597: // datagram not received yet fp@2597: return datagram_used; fp@2597: } fp@2597: fp@2597: fsm->state(fsm, datagram); fp@2597: fp@2597: datagram_used = fp@2597: fsm->state != ec_fsm_eoe_end && fsm->state != ec_fsm_eoe_error; fp@2597: fp@2597: if (datagram_used) { fp@2597: fsm->datagram = datagram; fp@2597: } else { fp@2597: fsm->datagram = NULL; fp@2597: } fp@2597: fp@2597: return datagram_used; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** Returns, if the state machine terminated with success. fp@2597: * fp@2597: * \return non-zero if successful. fp@2597: */ fp@2597: int ec_fsm_eoe_success(const ec_fsm_eoe_t *fsm /**< Finite state machine */) fp@2597: { fp@2597: return fsm->state == ec_fsm_eoe_end; fp@2597: } fp@2597: fp@2597: /****************************************************************************** fp@2597: * EoE set IP parameter state machine fp@2597: *****************************************************************************/ fp@2597: fp@2597: /** Prepare a set IP parameters operation. fp@2597: * fp@2597: * \return 0 on success, otherwise a negative error code. fp@2597: */ fp@2597: int ec_fsm_eoe_prepare_set( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: uint8_t *data, *cur; fp@2597: ec_slave_t *slave = fsm->slave; fp@2597: ec_master_t *master = slave->master; fp@2597: ec_eoe_request_t *req = fsm->request; fp@2597: size_t size = 8; fp@2597: fp@2597: if (req->mac_address_included) { fp@2597: size += ETH_ALEN; fp@2597: } fp@2597: fp@2597: if (req->ip_address_included) { fp@2597: size += 4; fp@2597: } fp@2597: fp@2597: if (req->subnet_mask_included) { fp@2597: size += 4; fp@2597: } fp@2597: fp@2597: if (req->gateway_included) { fp@2597: size += 4; fp@2597: } fp@2597: fp@2597: if (req->dns_included) { fp@2597: size += 4; fp@2597: } fp@2597: fp@2597: if (req->name_included) { fp@2597: size += EC_MAX_HOSTNAME_SIZE; fp@2597: } fp@2597: fp@2597: data = ec_slave_mbox_prepare_send(slave, datagram, EC_MBOX_TYPE_EOE, fp@2597: size); fp@2597: if (IS_ERR(data)) { fp@2597: return PTR_ERR(data); fp@2597: } fp@2597: fp@2597: EC_WRITE_U8(data, EC_EOE_FRAMETYPE_SET_IP_REQ); // Set IP parameter req. fp@2597: EC_WRITE_U8(data + 1, 0x01); // last fragment, no timestamps fp@2597: EC_WRITE_U16(data + 2, 0x0000); // fragment no., offset, frame no. fp@2597: fp@2597: EC_WRITE_U32(data + 4, fp@2597: ((req->mac_address_included != 0) << 0) | fp@2597: ((req->ip_address_included != 0) << 1) | fp@2597: ((req->subnet_mask_included != 0) << 2) | fp@2597: ((req->gateway_included != 0) << 3) | fp@2597: ((req->dns_included != 0) << 4) | fp@2597: ((req->name_included != 0) << 5) fp@2597: ); fp@2597: fp@2597: cur = data + 8; fp@2597: fp@2597: if (req->mac_address_included) { fp@2597: memcpy(cur, req->mac_address, ETH_ALEN); fp@2597: cur += ETH_ALEN; fp@2597: } fp@2597: fp@2597: if (req->ip_address_included) { fp@2642: uint32_t swapped = htonl(req->ip_address); fp@2642: memcpy(cur, &swapped, 4); fp@2597: cur += 4; fp@2597: } fp@2597: fp@2597: if (req->subnet_mask_included) { fp@2642: uint32_t swapped = htonl(req->subnet_mask); fp@2642: memcpy(cur, &swapped, 4); fp@2597: cur += 4; fp@2597: } fp@2597: fp@2597: if (req->gateway_included) { fp@2642: uint32_t swapped = htonl(req->gateway); fp@2642: memcpy(cur, &swapped, 4); fp@2597: cur += 4; fp@2597: } fp@2597: fp@2597: if (req->dns_included) { fp@2642: uint32_t swapped = htonl(req->dns); fp@2642: memcpy(cur, &swapped, 4); fp@2597: cur += 4; fp@2597: } fp@2597: fp@2597: if (req->name_included) { fp@2597: memcpy(cur, req->name, EC_MAX_HOSTNAME_SIZE); fp@2597: cur += EC_MAX_HOSTNAME_SIZE; fp@2597: } fp@2597: fp@2597: if (master->debug_level) { fp@2597: EC_SLAVE_DBG(slave, 0, "Set IP parameter request:\n"); fp@2597: ec_print_data(data, cur - data); fp@2597: } fp@2597: fp@2597: fsm->request->jiffies_sent = jiffies; fp@2597: fp@2597: return 0; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** EoE state: SET IP START. fp@2597: */ fp@2597: void ec_fsm_eoe_set_ip_start( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: ec_slave_t *slave = fsm->slave; fp@2597: fp@2597: EC_SLAVE_DBG(slave, 1, "Setting IP parameters.\n"); fp@2597: fp@2597: if (!(slave->sii.mailbox_protocols & EC_MBOX_EOE)) { fp@2597: EC_SLAVE_ERR(slave, "Slave does not support EoE!\n"); fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: return; fp@2597: } fp@2597: fp@2597: if (ec_fsm_eoe_prepare_set(fsm, datagram)) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: return; fp@2597: } fp@2597: fp@2597: fsm->retries = EC_FSM_RETRIES; fp@2597: fsm->state = ec_fsm_eoe_set_ip_request; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** EoE state: SET IP REQUEST. fp@2597: */ fp@2597: void ec_fsm_eoe_set_ip_request( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: ec_slave_t *slave = fsm->slave; fp@2597: fp@2597: if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { fp@2597: if (ec_fsm_eoe_prepare_set(fsm, datagram)) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: } fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Failed to receive EoE set IP parameter" fp@2597: " request: "); fp@2597: ec_datagram_print_state(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->working_counter != 1) { fp@2597: unsigned long diff_ms = fp@2597: (jiffies - fsm->request->jiffies_sent) * 1000 / HZ; fp@2597: fp@2597: if (!fsm->datagram->working_counter) { fp@2597: if (diff_ms < EC_EOE_RESPONSE_TIMEOUT) { fp@2597: // no response; send request datagram again fp@2597: if (ec_fsm_eoe_prepare_set(fsm, datagram)) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: } fp@2597: return; fp@2597: } fp@2597: } fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Reception of EoE set IP parameter request" fp@2597: " failed after %lu ms: ", diff_ms); fp@2597: ec_datagram_print_wc_error(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: fsm->jiffies_start = fsm->datagram->jiffies_sent; fp@2597: ec_slave_mbox_prepare_check(slave, datagram); // can not fail. fp@2597: fsm->retries = EC_FSM_RETRIES; fp@2597: fsm->state = ec_fsm_eoe_set_ip_check; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** EoE state: SET IP CHECK. fp@2597: */ fp@2597: void ec_fsm_eoe_set_ip_check( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: ec_slave_t *slave = fsm->slave; fp@2597: fp@2597: if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { fp@2597: ec_slave_mbox_prepare_check(slave, datagram); // can not fail. fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Failed to receive EoE mailbox check datagram: "); fp@2597: ec_datagram_print_state(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->working_counter != 1) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Reception of EoE mailbox check" fp@2597: " datagram failed: "); fp@2597: ec_datagram_print_wc_error(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: if (!ec_slave_mbox_check(fsm->datagram)) { fp@2597: unsigned long diff_ms = fp@2597: (fsm->datagram->jiffies_received - fsm->jiffies_start) * fp@2597: 1000 / HZ; fp@2597: if (diff_ms >= EC_EOE_RESPONSE_TIMEOUT) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting for" fp@2597: " set IP parameter response.\n", diff_ms); fp@2597: return; fp@2597: } fp@2597: fp@2597: ec_slave_mbox_prepare_check(slave, datagram); // can not fail. fp@2597: fsm->retries = EC_FSM_RETRIES; fp@2597: return; fp@2597: } fp@2597: fp@2597: // fetch response fp@2597: ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. fp@2597: fsm->retries = EC_FSM_RETRIES; fp@2597: fsm->state = ec_fsm_eoe_set_ip_response; fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** EoE state: SET IP RESPONSE. fp@2597: */ fp@2597: void ec_fsm_eoe_set_ip_response( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: ec_slave_t *slave = fsm->slave; fp@2597: ec_master_t *master = slave->master; fp@2597: uint8_t *data, mbox_prot, frame_type; fp@2597: size_t rec_size; fp@2597: ec_eoe_request_t *req = fsm->request; fp@2597: fp@2597: if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) { fp@2597: ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail. fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Failed to receive EoE read response datagram: "); fp@2597: ec_datagram_print_state(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: if (fsm->datagram->working_counter != 1) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Reception of EoE read response failed: "); fp@2597: ec_datagram_print_wc_error(fsm->datagram); fp@2597: return; fp@2597: } fp@2597: fp@2597: data = ec_slave_mbox_fetch(slave, fsm->datagram, &mbox_prot, &rec_size); fp@2597: if (IS_ERR(data)) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: return; fp@2597: } fp@2597: fp@2597: if (master->debug_level) { fp@2597: EC_SLAVE_DBG(slave, 0, "Set IP parameter response:\n"); fp@2597: ec_print_data(data, rec_size); fp@2597: } fp@2597: fp@2597: if (mbox_prot != EC_MBOX_TYPE_EOE) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n", fp@2597: mbox_prot); fp@2597: return; fp@2597: } fp@2597: fp@2597: if (rec_size < 4) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_ERR(slave, "Received currupted EoE set IP parameter response" fp@2597: " (%zu bytes)!\n", rec_size); fp@2597: ec_print_data(data, rec_size); fp@2597: return; fp@2597: } fp@2597: fp@2597: frame_type = EC_READ_U8(data) & 0x0f; fp@2597: fp@2597: if (frame_type != EC_EOE_FRAMETYPE_SET_IP_RES) { fp@2597: EC_SLAVE_ERR(slave, "Received no set IP parameter response" fp@2597: " (frame type %x).\n", frame_type); fp@2597: ec_print_data(data, rec_size); fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: return; fp@2597: } fp@2597: fp@2597: req->result = EC_READ_U16(data + 2); fp@2597: fp@2597: if (req->result) { fp@2597: fsm->state = ec_fsm_eoe_error; fp@2597: EC_SLAVE_DBG(slave, 1, "EoE set IP parameters failed with result code" fp@2597: " 0x%04X.\n", req->result); fp@2597: } else { fp@2597: fsm->state = ec_fsm_eoe_end; // success fp@2597: } fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** State: ERROR. fp@2597: */ fp@2597: void ec_fsm_eoe_error( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: } fp@2597: fp@2597: /*****************************************************************************/ fp@2597: fp@2597: /** State: END. fp@2597: */ fp@2597: void ec_fsm_eoe_end( fp@2597: ec_fsm_eoe_t *fsm, /**< finite state machine */ fp@2597: ec_datagram_t *datagram /**< Datagram to use. */ fp@2597: ) fp@2597: { fp@2597: } fp@2597: fp@2597: /*****************************************************************************/