Merge branch 'ethercat-backport_setip' into 'devel-1.6'

Backport Set IP command from master

See merge request etherlab.org/ethercat!112
This commit is contained in:
Florian Pose 2024-02-01 14:35:32 +00:00
commit 585070576c
26 changed files with 1515 additions and 97 deletions

View File

@ -21,6 +21,7 @@ COMMANDS := \
foe_read \
foe_write \
graph \
ip \
master \
pdos \
reg_read \

View File

@ -2058,6 +2058,9 @@ index and Y is the decimal alias address.
\end{description}
For some hints on how to configure these virtual interfaces, see
\autoref{sec:eoe-config}.
Frames sent to these interfaces are forwarded to the associated slaves by the
master. Frames, that are received by the slaves, are fetched by the master and
forwarded to the virtual interfaces.
@ -2183,11 +2186,54 @@ is executed each timer interrupt. This guarantees a constant bandwidth, but
poses the new problem of concurrent access to the master. The locking
mechanisms needed for this are introduced in \autoref{sec:concurr}.
\paragraph{Automatic Configuration}
\subsection{EoE Interface Configuration}
\label{sec:eoe-config}
By default, slaves are left in PREOP state, if no configuration is applied. If
an EoE interface link is set to ``up'', the requested slave's
application-layer state is automatically set to OP.
The configuration of the EoE network interfaces is a matter of using standard
Linux networking infrastructure commands like \lstinline+ifconfig+,
\lstinline+ip+ and \lstinline+brctl+. Though this lies not in the scope of
this document, some hints and examples are provided in this section.
In the below examples it is assumed, that there are two slaves (0 and 1) with
EoE support in the bus. The first decision to make is whether to use a bridged
or routed environment.
\paragraph{Bridging} A common solution is to create a bridge containing all EoE
interfaces:
\begin{lstlisting}
$ `\textbf{brctl addbr br0}`
$ `\textbf{ip addr add 192.168.100.1/24 dev br0}`
$ `\textbf{brctl addif br0 eoe0s0}`
$ `\textbf{brctl addif br0 eoe0s1}`
\end{lstlisting}
The above example allows to access IPv4 nodes using subnet 192.168.100.0/24
connected to the EtherCAT bus via EoE. Please note, that the example only
contains ad-hoc configuration commands: If the bus topology changes, the EoE
interfaces are re-created and have to be added to the bridge again. Therefore
it is highly recommended to use the networking configuration infrastructure of
the used Linux distribution to store this configuration permanently, so that
appearing EoE devices are added automatically.
\paragraph{Routing} Another possibility is to create an IP subnet for each EoE
interface:
\begin{lstlisting}
$ `\textbf{ip addr add 192.168.200.1/24 dev eoe0s0}`
$ `\textbf{ip addr add 192.168.201.1/24 dev eoe0s1}`
$ `\textbf{echo 1 > /proc/sys/net/ipv4/ip\_forward}`
\end{lstlisting}
This example is again only an ad-hoc configuration (see above). Please note,
that it is necessary to set the default gateways properly on the IP nodes
connected to the EoE slaves, if they shall be able to communicate between the
different EoE interfaces / IP networks.
\paragraph{Setting IP Parameters} If IP address and other parameters of the
EoE remote nodes (not the EoE interfaces on the master side) have to be set,
this can be achieved via the \lstinline+ethercat ip+ command-line tool (see
\autoref{sec:ipparam}).
%------------------------------------------------------------------------------
@ -2444,6 +2490,28 @@ created, if the \lstinline+udev+ Package is installed. See
%------------------------------------------------------------------------------
\subsection{Setting Ethernet-over-EtherCAT IP Parameters}
\label{sec:ipparam}
Slaves can have own IP stack implementations accessible via EoE. Since some of
them do not provide other mechanisms to set IP parameters (because they only
have an EtherCAT interface), there is a possibility to set the below
parameters via EoE:
\begin{itemize}
\item Ethernet MAC address\footnote{The MAC address of the virtual EoE remote
interface, not the one of the EtherCAT interface.},
\item IPv4 address,
\item IPv4 subnet mask,
\item IPv4 default gateway,
\item IPv4 DNS server,
\item DNS host name.
\end{itemize}
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_ip}
%------------------------------------------------------------------------------
\subsection{Master and Ethernet Devices}
\lstinputlisting[basicstyle=\ttfamily\footnotesize]{external/ethercat_master}

View File

@ -69,7 +69,7 @@ ec_master-objs := \
voe_handler.o
ifeq (@ENABLE_EOE@,1)
ec_master-objs += ethernet.o
ec_master-objs += eoe_request.o ethernet.o fsm_eoe.o
endif
ifeq (@ENABLE_DEBUG_IF@,1)

View File

@ -31,6 +31,7 @@ noinst_HEADERS = \
device.c device.h \
domain.c domain.h \
doxygen.c \
eoe_request.c eoe_request.h \
ethernet.c ethernet.h \
flag.c flag.h \
fmmu_config.c fmmu_config.h \
@ -38,6 +39,7 @@ noinst_HEADERS = \
foe_request.c foe_request.h \
fsm_change.c fsm_change.h \
fsm_coe.c fsm_coe.h \
fsm_eoe.c fsm_eoe.h \
fsm_foe.c fsm_foe.h \
fsm_master.c fsm_master.h \
fsm_pdo.c fsm_pdo.h \

71
master/eoe_request.c Normal file
View File

@ -0,0 +1,71 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
*****************************************************************************/
/** \file
* Ethernet-over-EtherCAT request functions.
*/
/*****************************************************************************/
#include <linux/module.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include "eoe_request.h"
/*****************************************************************************/
/** EoE request constructor.
*/
void ec_eoe_request_init(
ec_eoe_request_t *req /**< EoE request. */
)
{
INIT_LIST_HEAD(&req->list);
req->state = EC_INT_REQUEST_INIT;
req->jiffies_sent = 0U;
req->mac_address_included = 0;
req->ip_address_included = 0;
req->subnet_mask_included = 0;
req->gateway_included = 0;
req->dns_included = 0;
req->name_included = 0;
memset(req->mac_address, 0x00, ETH_ALEN);
req->ip_address = 0;
req->subnet_mask = 0;
req->gateway = 0;
req->dns = 0;
req->name[0] = 0x00;
req->result = 0x0000;
}
/*****************************************************************************/

77
master/eoe_request.h Normal file
View File

@ -0,0 +1,77 @@
/******************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
*****************************************************************************/
/**
\file
EtherCAT EoE request structure.
*/
/*****************************************************************************/
#ifndef __EC_EOE_REQUEST_H__
#define __EC_EOE_REQUEST_H__
#include <linux/list.h>
#include <linux/etherdevice.h> // ETH_ALEN
#include "globals.h"
/*****************************************************************************/
/** Ethernet-over-EtherCAT set IP parameter request.
*/
typedef struct {
struct list_head list; /**< List item. */
ec_internal_request_state_t state; /**< Request state. */
unsigned long jiffies_sent; /**< Jiffies, when the request was sent. */
uint8_t mac_address_included;
uint8_t ip_address_included;
uint8_t subnet_mask_included;
uint8_t gateway_included;
uint8_t dns_included;
uint8_t name_included;
unsigned char mac_address[ETH_ALEN];
uint32_t ip_address;
uint32_t subnet_mask;
uint32_t gateway;
uint32_t dns;
char name[EC_MAX_HOSTNAME_SIZE];
uint16_t result;
} ec_eoe_request_t;
/*****************************************************************************/
void ec_eoe_request_init(ec_eoe_request_t *);
/*****************************************************************************/
#endif

View File

@ -307,7 +307,7 @@ int ec_eoe_send(ec_eoe_t *eoe /**< EoE handler */)
if (IS_ERR(data))
return PTR_ERR(data);
EC_WRITE_U8 (data, 0x00); // eoe fragment req.
EC_WRITE_U8 (data, EC_EOE_FRAMETYPE_INIT_REQ); // Initiate EoE Request
EC_WRITE_U8 (data + 1, last_fragment);
EC_WRITE_U16(data + 2, ((eoe->tx_fragment_number & 0x3F) |
(complete_offset & 0x3F) << 6 |
@ -494,7 +494,7 @@ void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */)
frame_type = EC_READ_U16(data) & 0x000F;
if (frame_type != 0x00) {
if (frame_type != EC_EOE_FRAMETYPE_INIT_REQ) { // EoE Fragment Data
#if EOE_DEBUG_LEVEL >= 1
EC_SLAVE_WARN(eoe->slave, "%s: Other frame received."
" Dropping.\n", eoe->dev->name);

View File

@ -38,6 +38,18 @@
/****************************************************************************/
/** EoE frame types.
*/
enum {
EC_EOE_FRAMETYPE_INIT_REQ = 0x00, /** Initiate EoE Request. */
EC_EOE_FRAMETYPE_SET_IP_REQ = 0x02, /** Set IP Parameter Request. */
EC_EOE_FRAMETYPE_SET_IP_RES = 0x03, /** Set IP Parameter Response. */
EC_EOE_FRAMETYPE_FILT_REQ = 0x04, /** Set Address Filter Request. */
EC_EOE_FRAMETYPE_FILT_RES = 0x05, /** Set Address Filter Response. */
};
/*****************************************************************************/
/**
Queued frame structure.
*/

482
master/fsm_eoe.c Normal file
View File

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

74
master/fsm_eoe.h Normal file
View File

@ -0,0 +1,74 @@
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
****************************************************************************/
/**
\file
EtherCAT EoE set IP parameter state machines.
*/
/****************************************************************************/
#ifndef __EC_FSM_EOE_H__
#define __EC_FSM_EOE_H__
#include "globals.h"
#include "datagram.h"
#include "slave.h"
#include "eoe_request.h"
/****************************************************************************/
typedef struct ec_fsm_eoe ec_fsm_eoe_t; /**< \see ec_fsm_eoe */
/** Finite state machines for the Ethernet over EtherCAT protocol.
*/
struct ec_fsm_eoe {
ec_slave_t *slave; /**< slave the FSM runs on */
unsigned int retries; /**< retries upon datagram timeout */
void (*state)(ec_fsm_eoe_t *, ec_datagram_t *); /**< EoE state function */
ec_datagram_t *datagram; /**< Datagram used in the previous step. */
unsigned long jiffies_start; /**< Timestamp. */
ec_eoe_request_t *request; /**< EoE request */
};
/****************************************************************************/
void ec_fsm_eoe_init(ec_fsm_eoe_t *);
void ec_fsm_eoe_clear(ec_fsm_eoe_t *);
void ec_fsm_eoe_set_ip_param(ec_fsm_eoe_t *, ec_slave_t *,
ec_eoe_request_t *);
int ec_fsm_eoe_exec(ec_fsm_eoe_t *, ec_datagram_t *);
int ec_fsm_eoe_success(const ec_fsm_eoe_t *);
/****************************************************************************/
#endif

View File

@ -58,7 +58,7 @@ enum {
EC_FOE_OPCODE_ACK = 4, /**< Acknowledge. */
EC_FOE_OPCODE_ERR = 5, /**< Error. */
EC_FOE_OPCODE_BUSY = 6 /**< Busy. */
} ec_foe_opcode_t;
};
/****************************************************************************/

View File

@ -44,6 +44,10 @@ int ec_fsm_slave_action_process_foe(ec_fsm_slave_t *, ec_datagram_t *);
void ec_fsm_slave_state_foe_request(ec_fsm_slave_t *, ec_datagram_t *);
int ec_fsm_slave_action_process_soe(ec_fsm_slave_t *, ec_datagram_t *);
void ec_fsm_slave_state_soe_request(ec_fsm_slave_t *, ec_datagram_t *);
#ifdef EC_EOE
int ec_fsm_slave_action_process_eoe(ec_fsm_slave_t *, ec_datagram_t *);
void ec_fsm_slave_state_eoe_request(ec_fsm_slave_t *, ec_datagram_t *);
#endif
/****************************************************************************/
@ -63,11 +67,17 @@ void ec_fsm_slave_init(
fsm->reg_request = NULL;
fsm->foe_request = NULL;
fsm->soe_request = NULL;
#ifdef EC_EOE
fsm->eoe_request = NULL;
#endif
// Init sub-state-machines
ec_fsm_coe_init(&fsm->fsm_coe);
ec_fsm_foe_init(&fsm->fsm_foe);
ec_fsm_soe_init(&fsm->fsm_soe);
#ifdef EC_EOE
ec_fsm_eoe_init(&fsm->fsm_eoe);
#endif
}
/****************************************************************************/
@ -100,10 +110,20 @@ void ec_fsm_slave_clear(
wake_up_all(&fsm->slave->master->request_queue);
}
#ifdef EC_EOE
if (fsm->eoe_request) {
fsm->soe_request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&fsm->slave->master->request_queue);
}
#endif
// clear sub-state machines
ec_fsm_coe_clear(&fsm->fsm_coe);
ec_fsm_foe_clear(&fsm->fsm_foe);
ec_fsm_soe_clear(&fsm->fsm_soe);
#ifdef EC_EOE
ec_fsm_eoe_clear(&fsm->fsm_eoe);
#endif
}
/****************************************************************************/
@ -202,6 +222,13 @@ void ec_fsm_slave_state_ready(
if (ec_fsm_slave_action_process_soe(fsm, datagram)) {
return;
}
#ifdef EC_EOE
// Check for pending EoE IP parameter requests
if (ec_fsm_slave_action_process_eoe(fsm, datagram)) {
return;
}
#endif
}
/****************************************************************************/
@ -232,7 +259,7 @@ int ec_fsm_slave_action_process_sdo(
request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 1;
return 0;
}
if (slave->current_state == EC_SLAVE_STATE_INIT) {
@ -240,7 +267,7 @@ int ec_fsm_slave_action_process_sdo(
request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 1;
return 0;
}
fsm->sdo_request = request;
@ -334,7 +361,7 @@ int ec_fsm_slave_action_process_reg(
wake_up_all(&slave->master->request_queue);
fsm->reg_request = NULL;
fsm->state = ec_fsm_slave_state_idle;
return 1;
return 0;
}
// Found pending register request. Execute it!
@ -436,7 +463,7 @@ int ec_fsm_slave_action_process_foe(
request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 1;
return 0;
}
request->state = EC_INT_REQUEST_BUSY;
@ -513,7 +540,7 @@ int ec_fsm_slave_action_process_soe(
req->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 1;
return 0;
}
if (slave->current_state == EC_SLAVE_STATE_INIT) {
@ -572,3 +599,87 @@ void ec_fsm_slave_state_soe_request(
}
/****************************************************************************/
#ifdef EC_EOE
/** Check for pending EoE IP parameter requests and process one.
*
* \return non-zero, if a request is processed.
*/
int ec_fsm_slave_action_process_eoe(
ec_fsm_slave_t *fsm, /**< Slave state machine. */
ec_datagram_t *datagram /**< Datagram to use. */
)
{
ec_slave_t *slave = fsm->slave;
ec_eoe_request_t *request;
if (list_empty(&slave->eoe_requests)) {
return 0;
}
// take the first request to be processed
request = list_entry(slave->eoe_requests.next, ec_eoe_request_t, list);
list_del_init(&request->list); // dequeue
if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
EC_SLAVE_WARN(slave, "Aborting EoE request,"
" slave has error flag set.\n");
request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 0;
}
if (slave->current_state == EC_SLAVE_STATE_INIT) {
EC_SLAVE_WARN(slave, "Aborting EoE request, slave is in INIT.\n");
request->state = EC_INT_REQUEST_FAILURE;
wake_up_all(&slave->master->request_queue);
fsm->state = ec_fsm_slave_state_idle;
return 0;
}
fsm->eoe_request = request;
request->state = EC_INT_REQUEST_BUSY;
// Found pending request. Execute it!
EC_SLAVE_DBG(slave, 1, "Processing EoE request...\n");
// Start EoE command
fsm->state = ec_fsm_slave_state_eoe_request;
ec_fsm_eoe_set_ip_param(&fsm->fsm_eoe, slave, request);
ec_fsm_eoe_exec(&fsm->fsm_eoe, datagram); // execute immediately
return 1;
}
/****************************************************************************/
/** Slave state: EOE_REQUEST.
*/
void ec_fsm_slave_state_eoe_request(
ec_fsm_slave_t *fsm, /**< Slave state machine. */
ec_datagram_t *datagram /**< Datagram to use. */
)
{
ec_slave_t *slave = fsm->slave;
ec_eoe_request_t *req = fsm->eoe_request;
if (ec_fsm_eoe_exec(&fsm->fsm_eoe, datagram)) {
return;
}
if (ec_fsm_eoe_success(&fsm->fsm_eoe)) {
req->state = EC_INT_REQUEST_SUCCESS;
EC_SLAVE_DBG(slave, 1, "Finished EoE request.\n");
}
else {
req->state = EC_INT_REQUEST_FAILURE;
EC_SLAVE_ERR(slave, "Failed to process EoE request.\n");
}
wake_up_all(&slave->master->request_queue);
fsm->eoe_request = NULL;
fsm->state = ec_fsm_slave_state_ready;
}
/****************************************************************************/
#endif

View File

@ -33,9 +33,15 @@
#include "datagram.h"
#include "sdo_request.h"
#include "reg_request.h"
#ifdef EC_EOE
#include "eoe_request.h"
#endif
#include "fsm_coe.h"
#include "fsm_foe.h"
#include "fsm_soe.h"
#ifdef EC_EOE
#include "fsm_eoe.h"
#endif
/****************************************************************************/
@ -54,10 +60,16 @@ struct ec_fsm_slave {
ec_foe_request_t *foe_request; /**< FoE request to process. */
off_t foe_index; /**< Index to FoE write request data. */
ec_soe_request_t *soe_request; /**< SoE request to process. */
#ifdef EC_EOE
ec_eoe_request_t *eoe_request; /**< SoE request to process. */
#endif
ec_fsm_coe_t fsm_coe; /**< CoE state machine. */
ec_fsm_foe_t fsm_foe; /**< FoE state machine. */
ec_fsm_soe_t fsm_soe; /**< SoE state machine. */
#ifdef EC_EOE
ec_fsm_eoe_t fsm_eoe; /**< EoE state machine. */
#endif
};
/****************************************************************************/

View File

@ -35,7 +35,7 @@
/** SoE operations
*/
enum ec_soe_opcodes {
enum {
OPCODE_READ_REQUEST = 0x01, /**< Read request. */
OPCODE_READ_RESPONSE = 0x02, /**< Read response. */
OPCODE_WRITE_REQUEST = 0x03, /**< Write request. */

View File

@ -103,6 +103,12 @@
**/
#define EC_DATAGRAM_NAME_SIZE 20
/** Maximum hostname size.
*
* Used inside the EoE set IP parameter request.
*/
#define EC_MAX_HOSTNAME_SIZE 32
/** Slave state mask.
*
* Apply this mask to a slave state byte to get the slave state without
@ -130,6 +136,9 @@ typedef enum {
} ec_slave_state_t;
/** Supported mailbox protocols.
*
* Not to mix up with the mailbox type field in the mailbox header defined in
* master/mailbox.h.
*/
enum {
EC_MBOX_AOE = 0x01, /**< ADS over EtherCAT */

View File

@ -1622,6 +1622,91 @@ static ATTRIBUTES int ec_ioctl_eoe_handler(
/****************************************************************************/
#ifdef EC_EOE
/** Request EoE IP parameter setting.
*
* \return Zero on success, otherwise a negative error code.
*/
static ATTRIBUTES int ec_ioctl_slave_eoe_ip_param(
ec_master_t *master, /**< EtherCAT master. */
void *arg /**< ioctl() argument. */
)
{
ec_ioctl_slave_eoe_ip_t io;
ec_eoe_request_t req;
ec_slave_t *slave;
if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
return -EFAULT;
}
// init EoE request
ec_eoe_request_init(&req);
req.mac_address_included = io.mac_address_included;
req.ip_address_included = io.ip_address_included;
req.subnet_mask_included = io.subnet_mask_included;
req.gateway_included = io.gateway_included;
req.dns_included = io.dns_included;
req.name_included = io.name_included;
memcpy(req.mac_address, io.mac_address, ETH_ALEN);
req.ip_address = io.ip_address;
req.subnet_mask = io.subnet_mask;
req.gateway = io.gateway;
req.dns = io.dns;
memcpy(req.name, io.name, EC_MAX_HOSTNAME_SIZE);
req.state = EC_INT_REQUEST_QUEUED;
if (down_interruptible(&master->master_sem)) {
return -EINTR;
}
if (!(slave = ec_master_find_slave(
master, 0, io.slave_position))) {
up(&master->master_sem);
EC_MASTER_ERR(master, "Slave %u does not exist!\n",
io.slave_position);
return -EINVAL;
}
EC_MASTER_DBG(master, 1, "Scheduling EoE request.\n");
// schedule request.
list_add_tail(&req.list, &slave->eoe_requests);
up(&master->master_sem);
// wait for processing through FSM
if (wait_event_interruptible(master->request_queue,
req.state != EC_INT_REQUEST_QUEUED)) {
// interrupted by signal
down(&master->master_sem);
if (req.state == EC_INT_REQUEST_QUEUED) {
// abort request
list_del(&req.list);
up(&master->master_sem);
return -EINTR;
}
up(&master->master_sem);
}
// wait until master FSM has finished processing
wait_event(master->request_queue, req.state != EC_INT_REQUEST_BUSY);
io.result = req.result;
if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
return -EFAULT;
}
return req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
}
#endif
/*****************************************************************************/
/** Request the master from userspace.
*
* \return Zero on success, otherwise a negative error code.
@ -4686,6 +4771,15 @@ long EC_IOCTL(
case EC_IOCTL_SLAVE_SOE_READ:
ret = ec_ioctl_slave_soe_read(master, arg);
break;
#ifdef EC_EOE
case EC_IOCTL_SLAVE_EOE_IP_PARAM:
if (!ctx->writable) {
ret = -EPERM;
break;
}
ret = ec_ioctl_slave_eoe_ip_param(master, arg);
break;
#endif
case EC_IOCTL_SLAVE_SOE_WRITE:
if (!ctx->writable) {
ret = -EPERM;

View File

@ -47,7 +47,7 @@
*
* Increment this when changing the ioctl interface!
*/
#define EC_IOCTL_VERSION_MAGIC 34
#define EC_IOCTL_VERSION_MAGIC 35
// Command-line tool
#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)
@ -74,85 +74,88 @@
#define EC_IOCTL_SLAVE_FOE_WRITE EC_IOW(0x15, ec_ioctl_slave_foe_t)
#define EC_IOCTL_SLAVE_SOE_READ EC_IOWR(0x16, ec_ioctl_slave_soe_read_t)
#define EC_IOCTL_SLAVE_SOE_WRITE EC_IOWR(0x17, ec_ioctl_slave_soe_write_t)
#define EC_IOCTL_CONFIG EC_IOWR(0x18, ec_ioctl_config_t)
#define EC_IOCTL_CONFIG_PDO EC_IOWR(0x19, ec_ioctl_config_pdo_t)
#define EC_IOCTL_CONFIG_PDO_ENTRY EC_IOWR(0x1a, ec_ioctl_config_pdo_entry_t)
#define EC_IOCTL_CONFIG_SDO EC_IOWR(0x1b, ec_ioctl_config_sdo_t)
#define EC_IOCTL_CONFIG_IDN EC_IOWR(0x1c, ec_ioctl_config_idn_t)
#define EC_IOCTL_CONFIG_FLAG EC_IOWR(0x1d, ec_ioctl_config_flag_t)
#ifdef EC_EOE
#define EC_IOCTL_EOE_HANDLER EC_IOWR(0x1e, ec_ioctl_eoe_handler_t)
#define EC_IOCTL_SLAVE_EOE_IP_PARAM EC_IOW(0x18, ec_ioctl_slave_eoe_ip_t)
#endif
#define EC_IOCTL_CONFIG EC_IOWR(0x19, ec_ioctl_config_t)
#define EC_IOCTL_CONFIG_PDO EC_IOWR(0x1a, ec_ioctl_config_pdo_t)
#define EC_IOCTL_CONFIG_PDO_ENTRY EC_IOWR(0x1b, ec_ioctl_config_pdo_entry_t)
#define EC_IOCTL_CONFIG_SDO EC_IOWR(0x1c, ec_ioctl_config_sdo_t)
#define EC_IOCTL_CONFIG_IDN EC_IOWR(0x1d, ec_ioctl_config_idn_t)
#define EC_IOCTL_CONFIG_FLAG EC_IOWR(0x1e, ec_ioctl_config_flag_t)
#ifdef EC_EOE
#define EC_IOCTL_EOE_HANDLER EC_IOWR(0x1f, ec_ioctl_eoe_handler_t)
#endif
// Application interface
#define EC_IOCTL_REQUEST EC_IO(0x1f)
#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x20)
#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x21, ec_ioctl_config_t)
#define EC_IOCTL_SELECT_REF_CLOCK EC_IOW(0x22, uint32_t)
#define EC_IOCTL_ACTIVATE EC_IOR(0x23, ec_ioctl_master_activate_t)
#define EC_IOCTL_DEACTIVATE EC_IO(0x24)
#define EC_IOCTL_SEND EC_IO(0x25)
#define EC_IOCTL_RECEIVE EC_IO(0x26)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x27, ec_master_state_t)
#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x28, ec_ioctl_link_state_t)
#define EC_IOCTL_APP_TIME EC_IOW(0x29, uint64_t)
#define EC_IOCTL_SYNC_REF EC_IO(0x2a)
#define EC_IOCTL_SYNC_REF_TO EC_IOW(0x2b, uint64_t)
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x2c)
#define EC_IOCTL_REF_CLOCK_TIME EC_IOR(0x2d, uint32_t)
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2e)
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2f, uint32_t)
#define EC_IOCTL_RESET EC_IO(0x30)
#define EC_IOCTL_SC_SYNC EC_IOW(0x31, ec_ioctl_config_t)
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x32, ec_ioctl_config_t)
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x33, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x34, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x35, ec_ioctl_add_pdo_entry_t)
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x36, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x37, ec_ioctl_reg_pdo_entry_t)
#define EC_IOCTL_SC_REG_PDO_POS EC_IOWR(0x38, ec_ioctl_reg_pdo_pos_t)
#define EC_IOCTL_SC_DC EC_IOW(0x39, ec_ioctl_config_t)
#define EC_IOCTL_SC_SDO EC_IOW(0x3a, ec_ioctl_sc_sdo_t)
#define EC_IOCTL_SC_EMERG_SIZE EC_IOW(0x3b, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_POP EC_IOWR(0x3c, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x3d, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x3e, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x3f, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SC_SOE_REQUEST EC_IOWR(0x40, ec_ioctl_soe_request_t)
#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x41, ec_ioctl_reg_request_t)
#define EC_IOCTL_SC_VOE EC_IOWR(0x42, ec_ioctl_voe_t)
#define EC_IOCTL_SC_STATE EC_IOWR(0x43, ec_ioctl_sc_state_t)
#define EC_IOCTL_SC_IDN EC_IOW(0x44, ec_ioctl_sc_idn_t)
#define EC_IOCTL_SC_FLAG EC_IOW(0x45, ec_ioctl_sc_flag_t)
#define EC_IOCTL_DOMAIN_SIZE EC_IO(0x46)
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x47)
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x48)
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x49)
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x4a, ec_ioctl_domain_state_t)
#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x4b, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x4c, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x4d, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x4e, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x4f, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x50, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SOE_REQUEST_IDN EC_IOWR(0x51, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_TIMEOUT EC_IOWR(0x52, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_STATE EC_IOWR(0x53, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_READ EC_IOWR(0x54, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_WRITE EC_IOWR(0x55, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_DATA EC_IOWR(0x56, ec_ioctl_soe_request_t)
#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x57, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x58, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x59, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x5a, ec_ioctl_reg_request_t)
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x5b, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x5c, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ EC_IOW(0x5d, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x5e, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x5f, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x60, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_DATA EC_IOWR(0x61, ec_ioctl_voe_t)
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x62, size_t)
#define EC_IOCTL_REQUEST EC_IO(0x20)
#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x21)
#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x22, ec_ioctl_config_t)
#define EC_IOCTL_SELECT_REF_CLOCK EC_IOW(0x23, uint32_t)
#define EC_IOCTL_ACTIVATE EC_IOR(0x24, ec_ioctl_master_activate_t)
#define EC_IOCTL_DEACTIVATE EC_IO(0x25)
#define EC_IOCTL_SEND EC_IO(0x26)
#define EC_IOCTL_RECEIVE EC_IO(0x27)
#define EC_IOCTL_MASTER_STATE EC_IOR(0x28, ec_master_state_t)
#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x29, ec_ioctl_link_state_t)
#define EC_IOCTL_APP_TIME EC_IOW(0x2a, uint64_t)
#define EC_IOCTL_SYNC_REF EC_IO(0x2b)
#define EC_IOCTL_SYNC_REF_TO EC_IOW(0x2c, uint64_t)
#define EC_IOCTL_SYNC_SLAVES EC_IO(0x2d)
#define EC_IOCTL_REF_CLOCK_TIME EC_IOR(0x2e, uint32_t)
#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2f)
#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x30, uint32_t)
#define EC_IOCTL_RESET EC_IO(0x31)
#define EC_IOCTL_SC_SYNC EC_IOW(0x32, ec_ioctl_config_t)
#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x33, ec_ioctl_config_t)
#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x34, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x35, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x36, ec_ioctl_add_pdo_entry_t)
#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x37, ec_ioctl_config_pdo_t)
#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x38, ec_ioctl_reg_pdo_entry_t)
#define EC_IOCTL_SC_REG_PDO_POS EC_IOWR(0x39, ec_ioctl_reg_pdo_pos_t)
#define EC_IOCTL_SC_DC EC_IOW(0x3a, ec_ioctl_config_t)
#define EC_IOCTL_SC_SDO EC_IOW(0x3b, ec_ioctl_sc_sdo_t)
#define EC_IOCTL_SC_EMERG_SIZE EC_IOW(0x3c, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_POP EC_IOWR(0x3d, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x3e, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x3f, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x40, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SC_SOE_REQUEST EC_IOWR(0x41, ec_ioctl_soe_request_t)
#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x42, ec_ioctl_reg_request_t)
#define EC_IOCTL_SC_VOE EC_IOWR(0x43, ec_ioctl_voe_t)
#define EC_IOCTL_SC_STATE EC_IOWR(0x44, ec_ioctl_sc_state_t)
#define EC_IOCTL_SC_IDN EC_IOW(0x45, ec_ioctl_sc_idn_t)
#define EC_IOCTL_SC_FLAG EC_IOW(0x46, ec_ioctl_sc_flag_t)
#define EC_IOCTL_DOMAIN_SIZE EC_IO(0x47)
#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x48)
#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x49)
#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x4a)
#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x4b, ec_ioctl_domain_state_t)
#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x4c, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x4d, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x4e, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x4f, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x50, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x51, ec_ioctl_sdo_request_t)
#define EC_IOCTL_SOE_REQUEST_IDN EC_IOWR(0x52, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_TIMEOUT EC_IOWR(0x53, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_STATE EC_IOWR(0x54, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_READ EC_IOWR(0x55, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_WRITE EC_IOWR(0x56, ec_ioctl_soe_request_t)
#define EC_IOCTL_SOE_REQUEST_DATA EC_IOWR(0x57, ec_ioctl_soe_request_t)
#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x58, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x59, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x5a, ec_ioctl_reg_request_t)
#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x5b, ec_ioctl_reg_request_t)
#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x5c, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x5d, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ EC_IOW(0x5e, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x5f, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_WRITE EC_IOWR(0x60, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_EXEC EC_IOWR(0x61, ec_ioctl_voe_t)
#define EC_IOCTL_VOE_DATA EC_IOWR(0x62, ec_ioctl_voe_t)
#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x63, size_t)
/****************************************************************************/
@ -608,6 +611,37 @@ typedef struct {
/****************************************************************************/
#define EC_ETH_ALEN 6
#ifdef ETH_ALEN
#if ETH_ALEN != EC_ETH_ALEN
#error Ethernet address length mismatch
#endif
#endif
typedef struct {
// input
uint16_t slave_position;
uint8_t mac_address_included;
uint8_t ip_address_included;
uint8_t subnet_mask_included;
uint8_t gateway_included;
uint8_t dns_included;
uint8_t name_included;
unsigned char mac_address[EC_ETH_ALEN];
uint32_t ip_address;
uint32_t subnet_mask;
uint32_t gateway;
uint32_t dns;
char name[EC_MAX_HOSTNAME_SIZE];
// output
uint16_t result;
} ec_ioctl_slave_eoe_ip_t;
/*****************************************************************************/
typedef struct {
// outputs
void *process_data;

View File

@ -38,8 +38,10 @@
#define EC_MBOX_HEADER_SIZE 6
/** Mailbox types.
*
* These are used in the 'Type' field of the mailbox header.
*/
enum ec_mbox_types {
enum {
EC_MBOX_TYPE_EOE = 0x02,
EC_MBOX_TYPE_COE = 0x03,
EC_MBOX_TYPE_FOE = 0x04,

View File

@ -147,6 +147,7 @@ void ec_slave_init(
INIT_LIST_HEAD(&slave->reg_requests);
INIT_LIST_HEAD(&slave->foe_requests);
INIT_LIST_HEAD(&slave->soe_requests);
INIT_LIST_HEAD(&slave->eoe_requests);
// create state machine object
ec_fsm_slave_init(&slave->fsm, slave);
@ -203,6 +204,17 @@ void ec_slave_clear(ec_slave_t *slave /**< EtherCAT slave */)
request->state = EC_INT_REQUEST_FAILURE;
}
#ifdef EC_EOE
while (!list_empty(&slave->eoe_requests)) {
ec_eoe_request_t *request =
list_entry(slave->eoe_requests.next, ec_eoe_request_t, list);
list_del_init(&request->list); // dequeue
EC_SLAVE_WARN(slave, "Discarding EoE request,"
" slave about to be deleted.\n");
request->state = EC_INT_REQUEST_FAILURE;
}
#endif
wake_up_all(&slave->master->request_queue);
if (slave->config) {

View File

@ -220,8 +220,9 @@ struct ec_slave
struct list_head sdo_requests; /**< SDO access requests. */
struct list_head reg_requests; /**< Register access requests. */
struct list_head foe_requests; /**< FoE write requests. */
struct list_head soe_requests; /**< SoE write requests. */
struct list_head foe_requests; /**< FoE requests. */
struct list_head soe_requests; /**< SoE requests. */
struct list_head eoe_requests; /**< EoE set IP parameter requests. */
ec_fsm_slave_t fsm; /**< Slave state machine. */
};

258
tool/CommandIp.cpp Normal file
View File

@ -0,0 +1,258 @@
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
* vim: expandtab
*
****************************************************************************/
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <iostream>
#include <algorithm>
using namespace std;
#include "CommandIp.h"
#include "MasterDevice.h"
/*****************************************************************************/
CommandIp::CommandIp():
Command("ip", "Set EoE IP parameters.")
{
}
/*****************************************************************************/
string CommandIp::helpString(const string &binaryBaseName) const
{
stringstream str;
str << binaryBaseName << " " << getName() << " [OPTIONS] <ARGS>" << endl
<< endl
<< getBriefDescription() << endl
<< endl
<< "This command requires a single slave to be selected." << endl
<< endl
<< "IP parameters can be appended as argument pairs:" << endl
<< endl
<< " addr <IPv4>[/prefix] IP address (optionally with" << endl
<< " decimal subnet prefix)" << endl
<< " link <MAC> Link-layer address (may contain" << endl
<< " colons or hyphens)" << endl
<< " default <IPv4> Default gateway" << endl
<< " dns <IPv4> DNS server" << endl
<< " name <hostname> Host name (max. 32 byte)" << endl
<< endl
<< "IPv4 adresses can be given either in dot notation or as" << endl
<< "hostnames, which will be automatically resolved." << endl
<< endl
<< "Command-specific options:" << endl
<< " --alias -a <alias>" << endl
<< " --position -p <pos> Slave selection. See the help of" << endl
<< " the 'slaves' command." << endl
<< endl
<< numericInfo();
return str.str();
}
/****************************************************************************/
void CommandIp::execute(const StringVector &args)
{
if (args.size() <= 0) {
return;
}
if (args.size() % 2) {
stringstream err;
err << "'" << getName() << "' needs an even number of arguments!";
throwInvalidUsageException(err);
}
ec_ioctl_slave_eoe_ip_t io = {};
for (unsigned int argIdx = 0; argIdx < args.size(); argIdx += 2) {
string arg = args[argIdx];
string val = args[argIdx + 1];
std::transform(arg.begin(), arg.end(), arg.begin(), ::tolower);
if (arg == "link") {
parseMac(io.mac_address, val);
io.mac_address_included = 1;
}
else if (arg == "addr") {
parseIpv4Prefix(&io, val);
io.ip_address_included = 1;
}
else if (arg == "default") {
resolveIpv4(&io.gateway, val);
io.gateway_included = 1;
}
else if (arg == "dns") {
resolveIpv4(&io.dns, val);
io.dns_included = 1;
}
else if (arg == "name") {
if (val.size() > EC_MAX_HOSTNAME_SIZE - 1) {
stringstream err;
err << "Name too long!";
throwInvalidUsageException(err);
}
unsigned int i;
for (i = 0; i < val.size(); i++) {
io.name[i] = val[i];
}
io.name[i] = 0;
io.name_included = 1;
}
else {
stringstream err;
err << "Unknown argument '" << args[argIdx] << "'!";
throwInvalidUsageException(err);
}
}
MasterDevice m(getSingleMasterIndex());
m.open(MasterDevice::ReadWrite);
SlaveList slaves = selectedSlaves(m);
if (slaves.size() != 1) {
throwSingleSlaveRequired(slaves.size());
}
io.slave_position = slaves.front().position;
// execute actual request
try {
m.setIpParam(&io);
} catch (MasterDeviceException &e) {
throw e;
}
}
/*****************************************************************************/
void CommandIp::parseMac(unsigned char mac[EC_ETH_ALEN], const string &str)
{
unsigned int pos = 0;
for (unsigned int i = 0; i < EC_ETH_ALEN; i++) {
if (pos + 2 > str.size()) {
stringstream err;
err << "Incomplete MAC address!";
throwInvalidUsageException(err);
}
string byteStr = str.substr(pos, 2);
pos += 2;
stringstream s;
s << byteStr;
unsigned int byteValue;
s >> hex >> byteValue;
if (s.fail() || !s.eof() || byteValue > 0xff) {
stringstream err;
err << "Invalid MAC address!";
throwInvalidUsageException(err);
}
mac[i] = byteValue;
while (pos < str.size() && (str[pos] == ':' || str[pos] == '-')) {
pos++;
}
}
}
/*****************************************************************************/
void CommandIp::parseIpv4Prefix(ec_ioctl_slave_eoe_ip_t *io,
const string &str)
{
size_t pos = str.find('/');
string host;
io->subnet_mask_included = pos != string::npos;
if (pos == string::npos) { // no prefix found
host = str;
}
else {
host = str.substr(0, pos);
string prefixStr = str.substr(pos + 1, string::npos);
stringstream s;
s << prefixStr;
unsigned int prefix;
s >> prefix;
if (s.fail() || !s.eof() || prefix > 32) {
stringstream err;
err << "Invalid prefix '" << prefixStr << "'!";
throwInvalidUsageException(err);
}
uint32_t mask = 0;
for (unsigned int bit = 0; bit < prefix; bit++) {
mask |= (1 << (31 - bit));
}
io->subnet_mask = htonl(mask);
}
resolveIpv4(&io->ip_address, host);
}
/*****************************************************************************/
void CommandIp::resolveIpv4(uint32_t *addr, const string &str)
{
struct addrinfo hints = {};
struct addrinfo *res;
hints.ai_family = AF_INET; // only IPv4
int ret = getaddrinfo(str.c_str(), NULL, &hints, &res);
if (ret) {
stringstream err;
err << "Lookup of '" << str << "' failed: "
<< gai_strerror(ret) << endl;
throwCommandException(err.str());
}
if (!res) { // returned list is empty
stringstream err;
err << "Lookup of '" << str << "' failed." << endl;
throwCommandException(err.str());
}
sockaddr_in *sin = (sockaddr_in *) res->ai_addr;
for (unsigned int i = 0; i < 4; i++) {
((unsigned char *) addr)[i] =
((unsigned char *) &sin->sin_addr.s_addr)[i];
}
freeaddrinfo(res);
}
/****************************************************************************/

55
tool/CommandIp.h Normal file
View File

@ -0,0 +1,55 @@
/*****************************************************************************
*
* $Id$
*
* Copyright (C) 2006-2014 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
* The IgH EtherCAT Master is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*
* The IgH EtherCAT Master is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
* Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with the IgH EtherCAT Master; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
* ---
*
* The license mentioned above concerns the source code only. Using the
* EtherCAT technology and brand is only permitted in compliance with the
* industrial property and similar rights of Beckhoff Automation GmbH.
*
****************************************************************************/
#ifndef __COMMANDIP_H__
#define __COMMANDIP_H__
#include "Command.h"
/****************************************************************************/
class CommandIp:
public Command
{
public:
CommandIp();
string helpString(const string &) const;
void execute(const StringVector &);
protected:
void parseMac(unsigned char [6], const string &);
void parseIpv4Prefix(ec_ioctl_slave_eoe_ip_t *,
const string &);
void resolveIpv4(uint32_t *, const string &);
};
/****************************************************************************/
#endif

View File

@ -66,9 +66,9 @@ ethercat_SOURCES = \
sii_crc.cpp
if ENABLE_EOE
ethercat_SOURCES += CommandEoe.cpp
ethercat_SOURCES += CommandEoe.cpp CommandIp.cpp
else
EXTRA_DIST += CommandEoe.cpp
EXTRA_DIST += CommandEoe.cpp CommandIp.cpp
endif
noinst_HEADERS = \
@ -108,9 +108,9 @@ noinst_HEADERS = \
sii_crc.h
if ENABLE_EOE
noinst_HEADERS += CommandEoe.h
noinst_HEADERS += CommandEoe.h CommandIp.h
else
EXTRA_DIST += CommandEoe.h
EXTRA_DIST += CommandEoe.h CommandIp.h
endif
REV = `if test -s $(top_srcdir)/revision; then \

View File

@ -602,3 +602,20 @@ void MasterDevice::writeSoe(ec_ioctl_slave_soe_write_t *data)
}
/****************************************************************************/
#ifdef EC_EOE
void MasterDevice::setIpParam(ec_ioctl_slave_eoe_ip_t *data)
{
if (ioctl(fd, EC_IOCTL_SLAVE_EOE_IP_PARAM, data) < 0) {
if (errno == EIO && data->result) {
throw MasterDeviceEoeException(data->result);
} else {
stringstream err;
err << "Failed to set IP parameters: " << strerror(errno);
throw MasterDeviceException(err);
}
}
}
#endif
/****************************************************************************/

View File

@ -86,6 +86,25 @@ class MasterDeviceSoeException:
/****************************************************************************/
#ifdef EC_EOE
class MasterDeviceEoeException:
public MasterDeviceException
{
friend class MasterDevice;
public:
uint16_t result;
protected:
/** Constructor with error code parameter. */
MasterDeviceEoeException(uint16_t result):
MasterDeviceException("EoE set IP parameter failed."),
result(result) {};
};
#endif
/****************************************************************************/
class MasterDevice
{
public:
@ -138,6 +157,7 @@ class MasterDevice
#endif
void readSoe(ec_ioctl_slave_soe_read_t *);
void writeSoe(ec_ioctl_slave_soe_write_t *);
void setIpParam(ec_ioctl_slave_eoe_ip_t *);
unsigned int getMasterCount() const {return masterCount;}

View File

@ -41,6 +41,9 @@ using namespace std;
#include "CommandFoeRead.h"
#include "CommandFoeWrite.h"
#include "CommandGraph.h"
#ifdef EC_EOE
# include "CommandIp.h"
#endif
#include "CommandMaster.h"
#include "CommandPdos.h"
#include "CommandRegRead.h"
@ -282,6 +285,9 @@ int main(int argc, char **argv)
commandList.push_back(new CommandFoeRead());
commandList.push_back(new CommandFoeWrite());
commandList.push_back(new CommandGraph());
#ifdef EC_EOE
commandList.push_back(new CommandIp());
#endif
commandList.push_back(new CommandMaster());
commandList.push_back(new CommandPdos());
commandList.push_back(new CommandRegRead());