summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMinda Chen <minda.chen@starfivetech.com>2023-06-27 15:23:39 +0300
committerMinda Chen <minda.chen@starfivetech.com>2023-11-06 14:47:35 +0300
commitc606d37f6e41251c0de22144f07d2d0c17e96cd4 (patch)
treef798e13fe60929cadf77392fcb91bc87787a5f69
parent5322763afa20323701d14a95e2e07a5db3ae48b7 (diff)
downloadlinux-c606d37f6e41251c0de22144f07d2d0c17e96cd4.tar.xz
add ethercat codes.
Signed-off-by: Minda Chen <minda.chen@starfivetech.com>
-rw-r--r--include/net/ecdev.h79
-rw-r--r--net/Kconfig1
-rw-r--r--net/Makefile1
-rw-r--r--net/ethercat/Kconfig14
-rw-r--r--net/ethercat/Makefile8
-rw-r--r--net/ethercat/config.h97
-rw-r--r--net/ethercat/generic.c476
-rw-r--r--net/ethercat/globals.h64
-rw-r--r--net/ethercat/include/ecrt.h2340
-rw-r--r--net/ethercat/include/ectty.h114
-rw-r--r--net/ethercat/master/Kconfig19
-rw-r--r--net/ethercat/master/Makefile14
-rw-r--r--net/ethercat/master/cdev.c333
-rw-r--r--net/ethercat/master/cdev.h61
-rw-r--r--net/ethercat/master/coe_emerg_ring.c177
-rw-r--r--net/ethercat/master/coe_emerg_ring.h78
-rw-r--r--net/ethercat/master/datagram.c649
-rw-r--r--net/ethercat/master/datagram.h148
-rw-r--r--net/ethercat/master/datagram_pair.c201
-rw-r--r--net/ethercat/master/datagram_pair.h69
-rw-r--r--net/ethercat/master/debug.c266
-rw-r--r--net/ethercat/master/debug.h66
-rw-r--r--net/ethercat/master/device.c737
-rw-r--r--net/ethercat/master/device.h158
-rw-r--r--net/ethercat/master/domain.c717
-rw-r--r--net/ethercat/master/domain.h90
-rw-r--r--net/ethercat/master/doxygen.c89
-rw-r--r--net/ethercat/master/ethernet.c874
-rw-r--r--net/ethercat/master/ethernet.h125
-rw-r--r--net/ethercat/master/fmmu_config.c99
-rw-r--r--net/ethercat/master/fmmu_config.h66
-rw-r--r--net/ethercat/master/foe.h62
-rw-r--r--net/ethercat/master/foe_request.c238
-rw-r--r--net/ethercat/master/foe_request.h86
-rw-r--r--net/ethercat/master/fsm_change.c577
-rw-r--r--net/ethercat/master/fsm_change.h92
-rw-r--r--net/ethercat/master/fsm_coe.c2534
-rw-r--r--net/ethercat/master/fsm_coe.h82
-rw-r--r--net/ethercat/master/fsm_foe.c927
-rw-r--r--net/ethercat/master/fsm_foe.h94
-rw-r--r--net/ethercat/master/fsm_master.c1306
-rw-r--r--net/ethercat/master/fsm_master.h113
-rw-r--r--net/ethercat/master/fsm_pdo.c806
-rw-r--r--net/ethercat/master/fsm_pdo.h84
-rw-r--r--net/ethercat/master/fsm_pdo_entry.c541
-rw-r--r--net/ethercat/master/fsm_pdo_entry.h83
-rw-r--r--net/ethercat/master/fsm_sii.c490
-rw-r--r--net/ethercat/master/fsm_sii.h90
-rw-r--r--net/ethercat/master/fsm_slave.c582
-rw-r--r--net/ethercat/master/fsm_slave.h83
-rw-r--r--net/ethercat/master/fsm_slave_config.c1730
-rw-r--r--net/ethercat/master/fsm_slave_config.h86
-rw-r--r--net/ethercat/master/fsm_slave_scan.c1121
-rw-r--r--net/ethercat/master/fsm_slave_scan.h83
-rw-r--r--net/ethercat/master/fsm_soe.c846
-rw-r--r--net/ethercat/master/fsm_soe.h75
-rw-r--r--net/ethercat/master/globals.h317
-rw-r--r--net/ethercat/master/ioctl.c4646
-rw-r--r--net/ethercat/master/ioctl.h782
-rw-r--r--net/ethercat/master/mailbox.c210
-rw-r--r--net/ethercat/master/mailbox.h60
-rw-r--r--net/ethercat/master/master.c3321
-rw-r--r--net/ethercat/master/master.h392
-rw-r--r--net/ethercat/master/module.c675
-rw-r--r--net/ethercat/master/pdo.c317
-rw-r--r--net/ethercat/master/pdo.h75
-rw-r--r--net/ethercat/master/pdo_entry.c132
-rw-r--r--net/ethercat/master/pdo_entry.h66
-rw-r--r--net/ethercat/master/pdo_list.c346
-rw-r--r--net/ethercat/master/pdo_list.h79
-rw-r--r--net/ethercat/master/reg_request.c131
-rw-r--r--net/ethercat/master/reg_request.h67
-rw-r--r--net/ethercat/master/rtdm-ioctl.c4646
-rw-r--r--net/ethercat/master/rtdm.c241
-rw-r--r--net/ethercat/master/rtdm.h56
-rw-r--r--net/ethercat/master/sdo.c132
-rw-r--r--net/ethercat/master/sdo.h69
-rw-r--r--net/ethercat/master/sdo_entry.c77
-rw-r--r--net/ethercat/master/sdo_entry.h72
-rw-r--r--net/ethercat/master/sdo_request.c258
-rw-r--r--net/ethercat/master/sdo_request.h83
-rw-r--r--net/ethercat/master/slave.c1000
-rw-r--r--net/ethercat/master/slave.h273
-rw-r--r--net/ethercat/master/slave_config.c1269
-rw-r--r--net/ethercat/master/slave_config.h185
-rw-r--r--net/ethercat/master/soe_errors.c96
-rw-r--r--net/ethercat/master/soe_request.c254
-rw-r--r--net/ethercat/master/soe_request.h80
-rw-r--r--net/ethercat/master/sync.c178
-rw-r--r--net/ethercat/master/sync.h68
-rw-r--r--net/ethercat/master/sync_config.c63
-rw-r--r--net/ethercat/master/sync_config.h59
-rw-r--r--net/ethercat/master/voe_handler.c560
-rw-r--r--net/ethercat/master/voe_handler.h73
94 files changed, 42449 insertions, 0 deletions
diff --git a/include/net/ecdev.h b/include/net/ecdev.h
new file mode 100644
index 000000000000..8da467cd6641
--- /dev/null
+++ b/include/net/ecdev.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 interface for EtherCAT device drivers.
+ *
+ * \defgroup DeviceInterface EtherCAT Device Interface
+ *
+ * Master interface for EtherCAT-capable network device drivers. Through the
+ * EtherCAT device interface, EtherCAT-capable network device drivers are able
+ * to connect their device(s) to the master, pass received frames and notify
+ * the master about status changes. The master on his part, can send his
+ * frames through connected devices.
+ */
+
+/*****************************************************************************/
+
+#ifndef __ECDEV_H__
+#define __ECDEV_H__
+
+#include <linux/netdevice.h>
+
+/*****************************************************************************/
+
+struct ec_device;
+typedef struct ec_device ec_device_t; /**< \see ec_device */
+
+/** Device poll function type.
+ */
+typedef void (*ec_pollfunc_t)(struct net_device *);
+
+/******************************************************************************
+ * Offering/withdrawal functions
+ *****************************************************************************/
+
+ec_device_t *ecdev_offer(struct net_device *net_dev, ec_pollfunc_t poll,
+ struct module *module);
+void ecdev_withdraw(ec_device_t *device);
+
+/******************************************************************************
+ * Device methods
+ *****************************************************************************/
+
+int ecdev_open(ec_device_t *device);
+void ecdev_close(ec_device_t *device);
+void ecdev_receive(ec_device_t *device, const void *data, size_t size);
+void ecdev_set_link(ec_device_t *device, uint8_t state);
+uint8_t ecdev_get_link(const ec_device_t *device);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/Kconfig b/net/Kconfig
index 074472dfa94a..1374de4f9def 100644
--- a/net/Kconfig
+++ b/net/Kconfig
@@ -387,6 +387,7 @@ source "net/ceph/Kconfig"
source "net/nfc/Kconfig"
source "net/psample/Kconfig"
source "net/ife/Kconfig"
+source "net/ethercat/Kconfig"
config LWTUNNEL
bool "Network light weight tunnels"
diff --git a/net/Makefile b/net/Makefile
index fbfeb8a0bb37..2ace88ddf75b 100644
--- a/net/Makefile
+++ b/net/Makefile
@@ -79,3 +79,4 @@ obj-$(CONFIG_NET_NCSI) += ncsi/
obj-$(CONFIG_XDP_SOCKETS) += xdp/
obj-$(CONFIG_MPTCP) += mptcp/
obj-$(CONFIG_MCTP) += mctp/
+obj-y += ethercat/
diff --git a/net/ethercat/Kconfig b/net/ethercat/Kconfig
new file mode 100644
index 000000000000..62ad882da950
--- /dev/null
+++ b/net/ethercat/Kconfig
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ethercat
+#
+
+config ETHERCAT
+ tristate "ETHERCAT master driver"
+ select ETHERCAT_MASTER
+ default n
+ help
+ Support for ethercat master driver.
+ If unsure, say Y.
+
+source "net/ethercat/master/Kconfig"
diff --git a/net/ethercat/Makefile b/net/ethercat/Makefile
new file mode 100644
index 000000000000..ff0760b802be
--- /dev/null
+++ b/net/ethercat/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the ethercat driver.
+#
+
+obj-y += master/
+obj-$(CONFIG_ETHERCAT) += ethercat.o
+ethercat-y += generic.o
diff --git a/net/ethercat/config.h b/net/ethercat/config.h
new file mode 100644
index 000000000000..385071ae2194
--- /dev/null
+++ b/net/ethercat/config.h
@@ -0,0 +1,97 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Debug interfaces enabled */
+/* #undef EC_DEBUG_IF */
+
+/* Debug ring enabled */
+/* #undef EC_DEBUG_RING */
+
+/* EoE support enabled */
+#define EC_EOE 1
+
+/* Use CPU timestamp counter */
+#define EC_HAVE_CYCLES 1
+
+/* Use vendor id / product code wildcards */
+/* #undef EC_IDENT_WILDCARDS */
+
+/* Max. number of Ethernet devices per master */
+#define EC_MAX_NUM_DEVICES 1
+
+/* Read alias adresses from register */
+/* #undef EC_REGALIAS */
+
+/* RTDM interface enabled */
+/* #undef EC_RTDM */
+
+/* Output to syslog in RT context */
+#define EC_RT_SYSLOG 1
+
+/* Assign SII to PDI */
+#define EC_SII_ASSIGN 1
+
+/* Use hrtimer for scheduling */
+#define EC_USE_HRTIMER 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Name of package */
+#define PACKAGE "ethercat"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "fp@igh-essen.com"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "ethercat"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "ethercat 1.5.2"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "ethercat"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.5.2"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "1.5.2"
+
+#define cpu_khz (1500000)
diff --git a/net/ethercat/generic.c b/net/ethercat/generic.c
new file mode 100644
index 000000000000..4c7f02dcaebc
--- /dev/null
+++ b/net/ethercat/generic.c
@@ -0,0 +1,476 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 generic Ethernet device module.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/version.h>
+#include <linux/if_arp.h> /* ARPHRD_ETHER */
+#include <linux/etherdevice.h>
+
+#include "globals.h"
+#include <net/ecdev.h>
+
+#define PFX "ec_generic: "
+
+#define ETH_P_ETHERCAT 0x88A4
+
+#define EC_GEN_RX_BUF_SIZE 1600
+
+/*****************************************************************************/
+
+int __init ec_gen_init_module(void);
+void __exit ec_gen_cleanup_module(void);
+
+/*****************************************************************************/
+
+/** \cond */
+
+MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
+MODULE_DESCRIPTION("EtherCAT master generic Ethernet device module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EC_MASTER_VERSION);
+
+/** \endcond */
+
+struct list_head generic_devices;
+
+typedef struct {
+ struct list_head list;
+ struct net_device *netdev;
+ struct net_device *used_netdev;
+ struct socket *socket;
+ ec_device_t *ecdev;
+ uint8_t *rx_buf;
+} ec_gen_device_t;
+
+typedef struct {
+ struct list_head list;
+ struct net_device *netdev;
+ char name[IFNAMSIZ];
+ int ifindex;
+ uint8_t dev_addr[ETH_ALEN];
+} ec_gen_interface_desc_t;
+
+int ec_gen_device_open(ec_gen_device_t *);
+int ec_gen_device_stop(ec_gen_device_t *);
+int ec_gen_device_start_xmit(ec_gen_device_t *, struct sk_buff *);
+void ec_gen_device_poll(ec_gen_device_t *);
+
+/*****************************************************************************/
+
+static int ec_gen_netdev_open(struct net_device *dev)
+{
+ ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
+ return ec_gen_device_open(gendev);
+}
+
+/*****************************************************************************/
+
+static int ec_gen_netdev_stop(struct net_device *dev)
+{
+ ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
+ return ec_gen_device_stop(gendev);
+}
+
+/*****************************************************************************/
+
+static int ec_gen_netdev_start_xmit(
+ struct sk_buff *skb,
+ struct net_device *dev
+ )
+{
+ ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
+ return ec_gen_device_start_xmit(gendev, skb);
+}
+
+/*****************************************************************************/
+
+void ec_gen_poll(struct net_device *dev)
+{
+ ec_gen_device_t *gendev = *((ec_gen_device_t **) netdev_priv(dev));
+ ec_gen_device_poll(gendev);
+}
+
+/*****************************************************************************/
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+static const struct net_device_ops ec_gen_netdev_ops = {
+ .ndo_open = ec_gen_netdev_open,
+ .ndo_stop = ec_gen_netdev_stop,
+ .ndo_start_xmit = ec_gen_netdev_start_xmit,
+};
+#endif
+
+/*****************************************************************************/
+
+/** Init generic device.
+ */
+int ec_gen_device_init(
+ ec_gen_device_t *dev
+ )
+{
+ ec_gen_device_t **priv;
+ char null = 0x00;
+
+ dev->ecdev = NULL;
+ dev->socket = NULL;
+ dev->rx_buf = NULL;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+ dev->netdev = alloc_netdev(sizeof(ec_gen_device_t *), &null,
+ NET_NAME_UNKNOWN, ether_setup);
+#else
+ dev->netdev = alloc_netdev(sizeof(ec_gen_device_t *), &null, ether_setup);
+#endif
+ if (!dev->netdev) {
+ return -ENOMEM;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ dev->netdev->netdev_ops = &ec_gen_netdev_ops;
+#else
+ dev->netdev->open = ec_gen_netdev_open;
+ dev->netdev->stop = ec_gen_netdev_stop;
+ dev->netdev->hard_start_xmit = ec_gen_netdev_start_xmit;
+#endif
+
+ priv = netdev_priv(dev->netdev);
+ *priv = dev;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Clear generic device.
+ */
+void ec_gen_device_clear(
+ ec_gen_device_t *dev
+ )
+{
+ if (dev->ecdev) {
+ ecdev_close(dev->ecdev);
+ ecdev_withdraw(dev->ecdev);
+ }
+ if (dev->socket) {
+ sock_release(dev->socket);
+ }
+ free_netdev(dev->netdev);
+
+ if (dev->rx_buf) {
+ kfree(dev->rx_buf);
+ }
+}
+
+/*****************************************************************************/
+
+/** Creates a network socket.
+ */
+int ec_gen_device_create_socket(
+ ec_gen_device_t *dev,
+ ec_gen_interface_desc_t *desc
+ )
+{
+ int ret;
+ struct sockaddr_ll sa;
+
+ dev->rx_buf = kmalloc(EC_GEN_RX_BUF_SIZE, GFP_KERNEL);
+ if (!dev->rx_buf) {
+ return -ENOMEM;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0)
+ ret = sock_create_kern(&init_net, PF_PACKET, SOCK_RAW,
+ htons(ETH_P_ETHERCAT), &dev->socket);
+#else
+ ret = sock_create_kern(PF_PACKET, SOCK_RAW, htons(ETH_P_ETHERCAT),
+ &dev->socket);
+#endif
+ if (ret) {
+ printk(KERN_ERR PFX "Failed to create socket (ret = %i).\n", ret);
+ return ret;
+ }
+
+ printk(KERN_ERR PFX "Binding socket to interface %i (%s).\n",
+ desc->ifindex, desc->name);
+
+ memset(&sa, 0x00, sizeof(sa));
+ sa.sll_family = AF_PACKET;
+ sa.sll_protocol = htons(ETH_P_ETHERCAT);
+ sa.sll_ifindex = desc->ifindex;
+ ret = kernel_bind(dev->socket, (struct sockaddr *) &sa, sizeof(sa));
+ if (ret) {
+ printk(KERN_ERR PFX "Failed to bind() socket to interface"
+ " (ret = %i).\n", ret);
+ sock_release(dev->socket);
+ dev->socket = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Offer generic device to master.
+ */
+int ec_gen_device_offer(
+ ec_gen_device_t *dev,
+ ec_gen_interface_desc_t *desc
+ )
+{
+ int ret = 0;
+
+ dev->used_netdev = desc->netdev;
+ memcpy(dev->netdev->dev_addr, desc->dev_addr, ETH_ALEN);
+
+ dev->ecdev = ecdev_offer(dev->netdev, ec_gen_poll, THIS_MODULE);
+ if (dev->ecdev) {
+ if (ec_gen_device_create_socket(dev, desc)) {
+ ecdev_withdraw(dev->ecdev);
+ dev->ecdev = NULL;
+ } else if (ecdev_open(dev->ecdev)) {
+ ecdev_withdraw(dev->ecdev);
+ dev->ecdev = NULL;
+ } else {
+ ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev)); // FIXME
+ ret = 1;
+ }
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Open the device.
+ */
+int ec_gen_device_open(
+ ec_gen_device_t *dev
+ )
+{
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Stop the device.
+ */
+int ec_gen_device_stop(
+ ec_gen_device_t *dev
+ )
+{
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ec_gen_device_start_xmit(
+ ec_gen_device_t *dev,
+ struct sk_buff *skb
+ )
+{
+ struct msghdr msg;
+ struct kvec iov;
+ size_t len = skb->len;
+ int ret;
+
+ ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev));
+
+ iov.iov_base = skb->data;
+ iov.iov_len = len;
+ memset(&msg, 0, sizeof(msg));
+
+ ret = kernel_sendmsg(dev->socket, &msg, &iov, 1, len);
+
+ return ret == len ? NETDEV_TX_OK : NETDEV_TX_BUSY;
+}
+
+/*****************************************************************************/
+
+/** Polls the device.
+ */
+void ec_gen_device_poll(
+ ec_gen_device_t *dev
+ )
+{
+ struct msghdr msg;
+ struct kvec iov;
+ int ret, budget = 10; // FIXME
+
+ ecdev_set_link(dev->ecdev, netif_carrier_ok(dev->used_netdev));
+
+ do {
+ iov.iov_base = dev->rx_buf;
+ iov.iov_len = EC_GEN_RX_BUF_SIZE;
+ memset(&msg, 0, sizeof(msg));
+
+ ret = kernel_recvmsg(dev->socket, &msg, &iov, 1, iov.iov_len,
+ MSG_DONTWAIT);
+ if (ret > 0) {
+ ecdev_receive(dev->ecdev, dev->rx_buf, ret);
+ } else if (ret < 0) {
+ break;
+ }
+ budget--;
+ } while (budget);
+}
+
+/*****************************************************************************/
+
+/** Offer device.
+ */
+int offer_device(
+ ec_gen_interface_desc_t *desc
+ )
+{
+ ec_gen_device_t *gendev;
+ int ret = 0;
+
+ gendev = kmalloc(sizeof(ec_gen_device_t), GFP_KERNEL);
+ if (!gendev) {
+ return -ENOMEM;
+ }
+
+ ret = ec_gen_device_init(gendev);
+ if (ret) {
+ kfree(gendev);
+ return ret;
+ }
+
+ if (ec_gen_device_offer(gendev, desc)) {
+ list_add_tail(&gendev->list, &generic_devices);
+ } else {
+ ec_gen_device_clear(gendev);
+ kfree(gendev);
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Clear devices.
+ */
+void clear_devices(void)
+{
+ ec_gen_device_t *gendev, *next;
+
+ list_for_each_entry_safe(gendev, next, &generic_devices, list) {
+ list_del(&gendev->list);
+ ec_gen_device_clear(gendev);
+ kfree(gendev);
+ }
+}
+
+/*****************************************************************************/
+
+/** Module initialization.
+ *
+ * Initializes \a master_count masters.
+ * \return 0 on success, else < 0
+ */
+int __init ec_gen_init_module(void)
+{
+ int ret = 0;
+ struct list_head descs;
+ struct net_device *netdev;
+ ec_gen_interface_desc_t *desc, *next;
+
+ printk(KERN_INFO PFX "EtherCAT master generic Ethernet device module %s\n",
+ EC_MASTER_VERSION);
+
+ INIT_LIST_HEAD(&generic_devices);
+ INIT_LIST_HEAD(&descs);
+
+ read_lock(&dev_base_lock);
+ for_each_netdev(&init_net, netdev) {
+ if (netdev->type != ARPHRD_ETHER)
+ continue;
+ desc = kmalloc(sizeof(ec_gen_interface_desc_t), GFP_ATOMIC);
+ if (!desc) {
+ ret = -ENOMEM;
+ read_unlock(&dev_base_lock);
+ goto out_err;
+ }
+ strncpy(desc->name, netdev->name, IFNAMSIZ);
+ desc->netdev = netdev;
+ desc->ifindex = netdev->ifindex;
+ memcpy(desc->dev_addr, netdev->dev_addr, ETH_ALEN);
+ list_add_tail(&desc->list, &descs);
+ }
+ read_unlock(&dev_base_lock);
+
+ list_for_each_entry_safe(desc, next, &descs, list) {
+ ret = offer_device(desc);
+ if (ret) {
+ goto out_err;
+ }
+ kfree(desc);
+ }
+ return ret;
+
+out_err:
+ list_for_each_entry_safe(desc, next, &descs, list) {
+ list_del(&desc->list);
+ kfree(desc);
+ }
+ clear_devices();
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Module cleanup.
+ *
+ * Clears all master instances.
+ */
+void __exit ec_gen_cleanup_module(void)
+{
+ clear_devices();
+ printk(KERN_INFO PFX "Unloading.\n");
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+module_init(ec_gen_init_module);
+module_exit(ec_gen_cleanup_module);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/globals.h b/net/ethercat/globals.h
new file mode 100644
index 000000000000..b5927729a639
--- /dev/null
+++ b/net/ethercat/globals.h
@@ -0,0 +1,64 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Global definitions and macros.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_GLOBALS_H__
+#define __EC_GLOBALS_H__
+
+#include "config.h"
+
+/******************************************************************************
+ * Overall macros
+ *****************************************************************************/
+
+/** Helper macro for EC_STR(), literates a macro argument.
+ *
+ * \param X argument to literate.
+ */
+#define EC_LIT(X) #X
+
+/** Converts a macro argument to a string.
+ *
+ * \param X argument to stringify.
+ */
+#define EC_STR(X) EC_LIT(X)
+
+/** Master version string
+ */
+#define EC_MASTER_VERSION VERSION " " EC_STR(REV)
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/include/ecrt.h b/net/ethercat/include/ecrt.h
new file mode 100644
index 000000000000..9d673ecb278a
--- /dev/null
+++ b/net/ethercat/include/ecrt.h
@@ -0,0 +1,2340 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT master userspace library.
+ *
+ * The IgH EtherCAT master userspace library is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * The IgH EtherCAT master userspace library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the IgH EtherCAT master userspace library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ---
+ *
+ * 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 master application interface.
+ *
+ * \defgroup ApplicationInterface EtherCAT Application Interface
+ *
+ * EtherCAT interface for realtime applications. This interface is designed
+ * for realtime modules that want to use EtherCAT. There are functions to
+ * request a master, to map process data, to communicate with slaves via CoE
+ * and to configure and activate the bus.
+ *
+ * Changes in version 1.5.2:
+ *
+ * - Added redundancy_active flag to ec_domain_state_t.
+ * - Added ecrt_master_link_state() method and ec_master_link_state_t to query
+ * the state of a redundant link.
+ * - Added the EC_HAVE_REDUNDANCY define, to check, if the interface contains
+ * redundancy features.
+ * - Added ecrt_sdo_request_index() to change SDO index and subindex after
+ * handler creation.
+ * - Added interface for retrieving CoE emergency messages, i. e.
+ * ecrt_slave_config_emerg_size(), ecrt_slave_config_emerg_pop(),
+ * ecrt_slave_config_emerg_clear(), ecrt_slave_config_emerg_overruns() and
+ * the defines EC_HAVE_EMERGENCY and EC_COE_EMERGENCY_MSG_SIZE.
+ * - Added interface for direct EtherCAT register access: Added data type
+ * ec_reg_request_t and methods ecrt_slave_config_create_reg_request(),
+ * ecrt_reg_request_data(), ecrt_reg_request_state(),
+ * ecrt_reg_request_write(), ecrt_reg_request_read() and the feature flag
+ * EC_HAVE_REG_ACCESS.
+ * - Added method to select the reference clock,
+ * ecrt_master_select_reference_clock() and the feature flag
+ * EC_HAVE_SELECT_REF_CLOCK to check, if the method is available.
+ * - Added method to get the reference clock time,
+ * ecrt_master_reference_clock_time() and the feature flag
+ * EC_HAVE_REF_CLOCK_TIME to have the possibility to synchronize the master
+ * clock to the reference clock.
+ * - Changed the data types of the shift times in ecrt_slave_config_dc() to
+ * int32_t to correctly display negative shift times.
+ * - Added ecrt_slave_config_reg_pdo_entry_pos() and the feature flag
+ * EC_HAVE_REG_BY_POS for registering PDO entries with non-unique indices
+ * via their positions in the mapping.
+ *
+ * Changes in version 1.5:
+ *
+ * - Added the distributed clocks feature and the respective method
+ * ecrt_slave_config_dc() to configure a slave for cyclic operation, and
+ * ecrt_master_application_time(), ecrt_master_sync_reference_clock() and
+ * ecrt_master_sync_slave_clocks() for offset and drift compensation. The
+ * EC_TIMEVAL2NANO() macro can be used for epoch time conversion, while the
+ * ecrt_master_sync_monitor_queue() and ecrt_master_sync_monitor_process()
+ * methods can be used to monitor the synchrony.
+ * - Improved the callback mechanism. ecrt_master_callbacks() now takes two
+ * callback functions for sending and receiving datagrams.
+ * ecrt_master_send_ext() is used to execute the sending of non-application
+ * datagrams.
+ * - Added watchdog configuration (method ecrt_slave_config_watchdog(),
+ * #ec_watchdog_mode_t, \a watchdog_mode parameter in ec_sync_info_t and
+ * ecrt_slave_config_sync_manager()).
+ * - Added ecrt_slave_config_complete_sdo() method to download an SDO during
+ * configuration via CompleteAccess.
+ * - Added ecrt_master_deactivate() to remove the bus configuration.
+ * - Added ecrt_open_master() and ecrt_master_reserve() separation for
+ * userspace.
+ * - Added bus information interface (methods ecrt_master(),
+ * ecrt_master_get_slave(), ecrt_master_get_sync_manager(),
+ * ecrt_master_get_pdo() and ecrt_master_get_pdo_entry()) to get information
+ * about the currently connected slaves and the PDO entries provided.
+ * - Added ecrt_master_sdo_download(), ecrt_master_sdo_download_complete() and
+ * ecrt_master_sdo_upload() methods to let an application transfer SDOs
+ * before activating the master.
+ * - Changed the meaning of the negative return values of
+ * ecrt_slave_config_reg_pdo_entry() and ecrt_slave_config_sdo*().
+ * - Implemented the Vendor-specific over EtherCAT mailbox protocol. See
+ * ecrt_slave_config_create_voe_handler().
+ * - Renamed ec_sdo_request_state_t to #ec_request_state_t, because it is also
+ * used by VoE handlers.
+ * - Removed 'const' from argument of ecrt_sdo_request_state(), because the
+ * userspace library has to modify object internals.
+ * - Added 64-bit data access macros.
+ * - Added ecrt_slave_config_idn() method for storing SoE IDN configurations,
+ * and ecrt_master_read_idn() and ecrt_master_write_idn() to read/write IDNs
+ * ad-hoc via the user-space library.
+ * - Added ecrt_master_reset() to initiate retrying to configure slaves.
+ *
+ * @{
+ */
+
+/*****************************************************************************/
+
+#ifndef __ECRT_H__
+#define __ECRT_H__
+
+#ifdef __KERNEL__
+#include <asm/byteorder.h>
+#include <linux/types.h>
+#include <linux/time.h>
+#else
+#include <stdlib.h> // for size_t
+#include <stdint.h>
+#include <sys/time.h> // for struct timeval
+#endif
+
+/******************************************************************************
+ * Global definitions
+ *****************************************************************************/
+
+/** EtherCAT realtime interface major version number.
+ */
+#define ECRT_VER_MAJOR 1
+
+/** EtherCAT realtime interface minor version number.
+ */
+#define ECRT_VER_MINOR 5
+
+/** EtherCAT realtime interface version word generator.
+ */
+#define ECRT_VERSION(a, b) (((a) << 8) + (b))
+
+/** EtherCAT realtime interface version word.
+ */
+#define ECRT_VERSION_MAGIC ECRT_VERSION(ECRT_VER_MAJOR, ECRT_VER_MINOR)
+
+/******************************************************************************
+ * Feature flags
+ *****************************************************************************/
+
+/** Defined, if the redundancy features are available.
+ *
+ * I. e. if the \a redundancy_active flag in ec_domain_state_t and the
+ * ecrt_master_link_state() method are available.
+ */
+#define EC_HAVE_REDUNDANCY
+
+/** Defined, if the CoE emergency ring feature is available.
+ *
+ * I. e. if the ecrt_slave_config_emerg_*() methods are available.
+ */
+#define EC_HAVE_EMERGENCY
+
+/** Defined, if the register access interface is available.
+ *
+ * I. e. if the methods ecrt_slave_config_create_reg_request(),
+ * ecrt_reg_request_data(), ecrt_reg_request_state(), ecrt_reg_request_write()
+ * and ecrt_reg_request_read() are available.
+ */
+#define EC_HAVE_REG_ACCESS
+
+/** Defined if the method ecrt_master_select_reference_clock() is available.
+ */
+#define EC_HAVE_SELECT_REF_CLOCK
+
+/** Defined if the method ecrt_master_reference_clock_time() is available.
+ */
+#define EC_HAVE_REF_CLOCK_TIME
+
+/** Defined if the method ecrt_slave_config_reg_pdo_entry_pos() is available.
+ */
+#define EC_HAVE_REG_BY_POS
+
+/** Defined if the method ecrt_master_sync_reference_clock_to() is available.
+ */
+#define EC_HAVE_SYNC_TO
+
+/*****************************************************************************/
+
+/** End of list marker.
+ *
+ * This can be used with ecrt_slave_config_pdos().
+ */
+#define EC_END ~0U
+
+/** Maximum number of sync managers per slave.
+ */
+#define EC_MAX_SYNC_MANAGERS 16
+
+/** Maximum string length.
+ *
+ * Used in ec_slave_info_t.
+ */
+#define EC_MAX_STRING_LENGTH 64
+
+/** Maximum number of slave ports. */
+#define EC_MAX_PORTS 4
+
+/** Timeval to nanoseconds conversion.
+ *
+ * This macro converts a Unix epoch time to EtherCAT DC time.
+ *
+ * \see void ecrt_master_application_time()
+ *
+ * \param TV struct timeval containing epoch time.
+ */
+#define EC_TIMEVAL2NANO(TV) \
+ (((TV).tv_sec - 946684800ULL) * 1000000000ULL + (TV).tv_usec * 1000ULL)
+
+/** Size of a CoE emergency message in byte.
+ *
+ * \see ecrt_slave_config_emerg_pop().
+ */
+#define EC_COE_EMERGENCY_MSG_SIZE 8
+
+/******************************************************************************
+ * Data types
+ *****************************************************************************/
+
+struct ec_master;
+typedef struct ec_master ec_master_t; /**< \see ec_master */
+
+struct ec_slave_config;
+typedef struct ec_slave_config ec_slave_config_t; /**< \see ec_slave_config */
+
+struct ec_domain;
+typedef struct ec_domain ec_domain_t; /**< \see ec_domain */
+
+struct ec_sdo_request;
+typedef struct ec_sdo_request ec_sdo_request_t; /**< \see ec_sdo_request. */
+
+struct ec_voe_handler;
+typedef struct ec_voe_handler ec_voe_handler_t; /**< \see ec_voe_handler. */
+
+struct ec_reg_request;
+typedef struct ec_reg_request ec_reg_request_t; /**< \see ec_sdo_request. */
+
+/*****************************************************************************/
+
+/** Master state.
+ *
+ * This is used for the output parameter of ecrt_master_state().
+ *
+ * \see ecrt_master_state().
+ */
+typedef struct {
+ unsigned int slaves_responding; /**< Sum of responding slaves on all
+ Ethernet devices. */
+ unsigned int al_states : 4; /**< Application-layer states of all slaves.
+ The states are coded in the lower 4 bits.
+ If a bit is set, it means that at least one
+ slave in the bus is in the corresponding
+ state:
+ - Bit 0: \a INIT
+ - Bit 1: \a PREOP
+ - Bit 2: \a SAFEOP
+ - Bit 3: \a OP */
+ unsigned int link_up : 1; /**< \a true, if at least one Ethernet link is
+ up. */
+} ec_master_state_t;
+
+/*****************************************************************************/
+
+/** Redundant link state.
+ *
+ * This is used for the output parameter of ecrt_master_link_state().
+ *
+ * \see ecrt_master_link_state().
+ */
+typedef struct {
+ unsigned int slaves_responding; /**< Sum of responding slaves on the given
+ link. */
+ unsigned int al_states : 4; /**< Application-layer states of the slaves on
+ the given link. The states are coded in the
+ lower 4 bits. If a bit is set, it means
+ that at least one slave in the bus is in the
+ corresponding state:
+ - Bit 0: \a INIT
+ - Bit 1: \a PREOP
+ - Bit 2: \a SAFEOP
+ - Bit 3: \a OP */
+ unsigned int link_up : 1; /**< \a true, if the given Ethernet link is up.
+ */
+} ec_master_link_state_t;
+
+/*****************************************************************************/
+
+/** Slave configuration state.
+ *
+ * This is used as an output parameter of ecrt_slave_config_state().
+ *
+ * \see ecrt_slave_config_state().
+ */
+typedef struct {
+ unsigned int online : 1; /**< The slave is online. */
+ unsigned int operational : 1; /**< The slave was brought into \a OP state
+ using the specified configuration. */
+ unsigned int al_state : 4; /**< The application-layer state of the slave.
+ - 1: \a INIT
+ - 2: \a PREOP
+ - 4: \a SAFEOP
+ - 8: \a OP
+
+ Note that each state is coded in a different
+ bit! */
+} ec_slave_config_state_t;
+
+/*****************************************************************************/
+
+/** Master information.
+ *
+ * This is used as an output parameter of ecrt_master().
+ *
+ * \see ecrt_master().
+ */
+typedef struct {
+ unsigned int slave_count; /**< Number of slaves in the bus. */
+ unsigned int link_up : 1; /**< \a true, if the network link is up. */
+ uint8_t scan_busy; /**< \a true, while the master is scanning the bus */
+ uint64_t app_time; /**< Application time. */
+} ec_master_info_t;
+
+/*****************************************************************************/
+
+/** EtherCAT slave port descriptor.
+ */
+typedef enum {
+ EC_PORT_NOT_IMPLEMENTED, /**< Port is not implemented. */
+ EC_PORT_NOT_CONFIGURED, /**< Port is not configured. */
+ EC_PORT_EBUS, /**< Port is an E-Bus. */
+ EC_PORT_MII /**< Port is a MII. */
+} ec_slave_port_desc_t;
+
+/*****************************************************************************/
+
+/** EtherCAT slave port information.
+ */
+typedef struct {
+ uint8_t link_up; /**< Link detected. */
+ uint8_t loop_closed; /**< Loop closed. */
+ uint8_t signal_detected; /**< Detected signal on RX port. */
+} ec_slave_port_link_t;
+
+/*****************************************************************************/
+
+/** Slave information.
+ *
+ * This is used as an output parameter of ecrt_master_get_slave().
+ *
+ * \see ecrt_master_get_slave().
+ */
+typedef struct {
+ uint16_t position; /**< Offset of the slave in the ring. */
+ uint32_t vendor_id; /**< Vendor-ID stored on the slave. */
+ uint32_t product_code; /**< Product-Code stored on the slave. */
+ uint32_t revision_number; /**< Revision-Number stored on the slave. */
+ uint32_t serial_number; /**< Serial-Number stored on the slave. */
+ uint16_t alias; /**< The slaves alias if not equal to 0. */
+ int16_t current_on_ebus; /**< Used current in mA. */
+ struct {
+ ec_slave_port_desc_t desc; /**< Physical port type. */
+ ec_slave_port_link_t link; /**< Port link state. */
+ uint32_t receive_time; /**< Receive time on DC transmission delay
+ measurement. */
+ uint16_t next_slave; /**< Ring position of next DC slave on that
+ port. */
+ uint32_t delay_to_next_dc; /**< Delay [ns] to next DC slave. */
+ } ports[EC_MAX_PORTS]; /**< Port information. */
+ uint8_t al_state; /**< Current state of the slave. */
+ uint8_t error_flag; /**< Error flag for that slave. */
+ uint8_t sync_count; /**< Number of sync managers. */
+ uint16_t sdo_count; /**< Number of SDOs. */
+ char name[EC_MAX_STRING_LENGTH]; /**< Name of the slave. */
+} ec_slave_info_t;
+
+/*****************************************************************************/
+
+/** Domain working counter interpretation.
+ *
+ * This is used in ec_domain_state_t.
+ */
+typedef enum {
+ EC_WC_ZERO = 0, /**< No registered process data were exchanged. */
+ EC_WC_INCOMPLETE, /**< Some of the registered process data were
+ exchanged. */
+ EC_WC_COMPLETE /**< All registered process data were exchanged. */
+} ec_wc_state_t;
+
+/*****************************************************************************/
+
+/** Domain state.
+ *
+ * This is used for the output parameter of ecrt_domain_state().
+ */
+typedef struct {
+ unsigned int working_counter; /**< Value of the last working counter. */
+ ec_wc_state_t wc_state; /**< Working counter interpretation. */
+ unsigned int redundancy_active; /**< Redundant link is in use. */
+} ec_domain_state_t;
+
+/*****************************************************************************/
+
+/** Direction type for PDO assignment functions.
+ */
+typedef enum {
+ EC_DIR_INVALID, /**< Invalid direction. Do not use this value. */
+ EC_DIR_OUTPUT, /**< Values written by the master. */
+ EC_DIR_INPUT, /**< Values read by the master. */
+ EC_DIR_COUNT /**< Number of directions. For internal use only. */
+} ec_direction_t;
+
+/*****************************************************************************/
+
+/** Watchdog mode for sync manager configuration.
+ *
+ * Used to specify, if a sync manager's watchdog is to be enabled.
+ */
+typedef enum {
+ EC_WD_DEFAULT, /**< Use the default setting of the sync manager. */
+ EC_WD_ENABLE, /**< Enable the watchdog. */
+ EC_WD_DISABLE, /**< Disable the watchdog. */
+} ec_watchdog_mode_t;
+
+/*****************************************************************************/
+
+/** PDO entry configuration information.
+ *
+ * This is the data type of the \a entries field in ec_pdo_info_t.
+ *
+ * \see ecrt_slave_config_pdos().
+ */
+typedef struct {
+ uint16_t index; /**< PDO entry index. */
+ uint8_t subindex; /**< PDO entry subindex. */
+ uint8_t bit_length; /**< Size of the PDO entry in bit. */
+} ec_pdo_entry_info_t;
+
+/*****************************************************************************/
+
+/** PDO configuration information.
+ *
+ * This is the data type of the \a pdos field in ec_sync_info_t.
+ *
+ * \see ecrt_slave_config_pdos().
+ */
+typedef struct {
+ uint16_t index; /**< PDO index. */
+ unsigned int n_entries; /**< Number of PDO entries in \a entries to map.
+ Zero means, that the default mapping shall be
+ used (this can only be done if the slave is
+ present at bus configuration time). */
+ ec_pdo_entry_info_t *entries; /**< Array of PDO entries to map. Can either
+ be \a NULL, or must contain at
+ least \a n_entries values. */
+} ec_pdo_info_t;
+
+/*****************************************************************************/
+
+/** Sync manager configuration information.
+ *
+ * This can be use to configure multiple sync managers including the PDO
+ * assignment and PDO mapping. It is used as an input parameter type in
+ * ecrt_slave_config_pdos().
+ */
+typedef struct {
+ uint8_t index; /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS for a valid sync manager,
+ but can also be \a 0xff to mark the end of the list. */
+ ec_direction_t dir; /**< Sync manager direction. */
+ unsigned int n_pdos; /**< Number of PDOs in \a pdos. */
+ ec_pdo_info_t *pdos; /**< Array with PDOs to assign. This must contain
+ at least \a n_pdos PDOs. */
+ ec_watchdog_mode_t watchdog_mode; /**< Watchdog mode. */
+} ec_sync_info_t;
+
+/*****************************************************************************/
+
+/** List record type for PDO entry mass-registration.
+ *
+ * This type is used for the array parameter of the
+ * ecrt_domain_reg_pdo_entry_list()
+ */
+typedef struct {
+ uint16_t alias; /**< Slave alias address. */
+ uint16_t position; /**< Slave position. */
+ uint32_t vendor_id; /**< Slave vendor ID. */
+ uint32_t product_code; /**< Slave product code. */
+ uint16_t index; /**< PDO entry index. */
+ uint8_t subindex; /**< PDO entry subindex. */
+ unsigned int *offset; /**< Pointer to a variable to store the PDO entry's
+ (byte-)offset in the process data. */
+ unsigned int *bit_position; /**< Pointer to a variable to store a bit
+ position (0-7) within the \a offset. Can be
+ NULL, in which case an error is raised if the
+ PDO entry does not byte-align. */
+} ec_pdo_entry_reg_t;
+
+/*****************************************************************************/
+
+/** Request state.
+ *
+ * This is used as return type for ecrt_sdo_request_state() and
+ * ecrt_voe_handler_state().
+ */
+typedef enum {
+ EC_REQUEST_UNUSED, /**< Not requested. */
+ EC_REQUEST_BUSY, /**< Request is being processed. */
+ EC_REQUEST_SUCCESS, /**< Request was processed successfully. */
+ EC_REQUEST_ERROR, /**< Request processing failed. */
+} ec_request_state_t;
+
+/*****************************************************************************/
+
+/** Application-layer state.
+ */
+typedef enum {
+ EC_AL_STATE_INIT = 1, /**< Init. */
+ EC_AL_STATE_PREOP = 2, /**< Pre-operational. */
+ EC_AL_STATE_SAFEOP = 4, /**< Safe-operational. */
+ EC_AL_STATE_OP = 8, /**< Operational. */
+} ec_al_state_t;
+
+/******************************************************************************
+ * Global functions
+ *****************************************************************************/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Returns the version magic of the realtime interface.
+ *
+ * \return Value of ECRT_VERSION_MAGIC() at EtherCAT master compile time.
+ */
+unsigned int ecrt_version_magic(void);
+
+/** Requests an EtherCAT master for realtime operation.
+ *
+ * Before an application can access an EtherCAT master, it has to reserve one
+ * for exclusive use.
+ *
+ * In userspace, this is a convenience function for ecrt_open_master() and
+ * ecrt_master_reserve().
+ *
+ * This function has to be the first function an application has to call to
+ * use EtherCAT. The function takes the index of the master as its argument.
+ * The first master has index 0, the n-th master has index n - 1. The number
+ * of masters has to be specified when loading the master module.
+ *
+ * \return Pointer to the reserved master, otherwise \a NULL.
+ */
+ec_master_t *ecrt_request_master(
+ unsigned int master_index /**< Index of the master to request. */
+ );
+
+#ifndef __KERNEL__
+
+/** Opens an EtherCAT master for userspace access.
+ *
+ * This function has to be the first function an application has to call to
+ * use EtherCAT. The function takes the index of the master as its argument.
+ * The first master has index 0, the n-th master has index n - 1. The number
+ * of masters has to be specified when loading the master module.
+ *
+ * For convenience, the function ecrt_request_master() can be used.
+ *
+ * \return Pointer to the opened master, otherwise \a NULL.
+ */
+ec_master_t *ecrt_open_master(
+ unsigned int master_index /**< Index of the master to request. */
+ );
+
+#endif // #ifndef __KERNEL__
+
+/** Releases a requested EtherCAT master.
+ *
+ * After use, a master it has to be released to make it available for other
+ * applications.
+ *
+ * This method frees all created data structures. It should not be called in
+ * realtime context.
+ *
+ * If the master was activated, ecrt_master_deactivate() is called internally.
+ */
+void ecrt_release_master(
+ ec_master_t *master /**< EtherCAT master */
+ );
+
+/******************************************************************************
+ * Master methods
+ *****************************************************************************/
+
+#ifndef __KERNEL__
+
+/** Reserves an EtherCAT master for realtime operation.
+ *
+ * Before an application can use PDO/domain registration functions or SDO
+ * request functions on the master, it has to reserve one for exclusive use.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ecrt_master_reserve(
+ ec_master_t *master /**< EtherCAT master */
+ );
+
+#endif // #ifndef __KERNEL__
+
+#ifdef __KERNEL__
+
+/** Sets the locking callbacks.
+ *
+ * For concurrent master access, i. e. if other instances than the application
+ * want to send and receive datagrams on the bus, the application has to
+ * provide a callback mechanism. This method takes two function pointers as
+ * its parameters. Asynchronous master access (like EoE processing) is only
+ * possible if the callbacks have been set.
+ *
+ * The task of the send callback (\a send_cb) is to decide, if the bus is
+ * currently accessible and whether or not to call the ecrt_master_send_ext()
+ * method.
+ *
+ * The task of the receive callback (\a receive_cb) is to decide, if a call to
+ * ecrt_master_receive() is allowed and to execute it respectively.
+ *
+ * \attention This method has to be called before ecrt_master_activate().
+ */
+void ecrt_master_callbacks(
+ ec_master_t *master, /**< EtherCAT master */
+ void (*send_cb)(void *), /**< Datagram sending callback. */
+ void (*receive_cb)(void *), /**< Receive callback. */
+ void *cb_data /**< Arbitrary pointer passed to the callback functions.
+ */
+ );
+
+#endif /* __KERNEL__ */
+
+/** Creates a new process data domain.
+ *
+ * For process data exchange, at least one process data domain is needed.
+ * This method creates a new process data domain and returns a pointer to the
+ * new domain object. This object can be used for registering PDOs and
+ * exchanging them in cyclic operation.
+ *
+ * This method allocates memory and should be called in non-realtime context
+ * before ecrt_master_activate().
+ *
+ * \return Pointer to the new domain on success, else NULL.
+ */
+ec_domain_t *ecrt_master_create_domain(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Obtains a slave configuration.
+ *
+ * Creates a slave configuration object for the given \a alias and \a position
+ * tuple and returns it. If a configuration with the same \a alias and \a
+ * position already exists, it will be re-used. In the latter case, the given
+ * vendor ID and product code are compared to the stored ones. On mismatch, an
+ * error message is raised and the function returns \a NULL.
+ *
+ * Slaves are addressed with the \a alias and \a position parameters.
+ * - If \a alias is zero, \a position is interpreted as the desired slave's
+ * ring position.
+ * - If \a alias is non-zero, it matches a slave with the given alias. In this
+ * case, \a position is interpreted as ring offset, starting from the
+ * aliased slave, so a position of zero means the aliased slave itself and a
+ * positive value matches the n-th slave behind the aliased one.
+ *
+ * If the slave with the given address is found during the bus configuration,
+ * its vendor ID and product code are matched against the given value. On
+ * mismatch, the slave is not configured and an error message is raised.
+ *
+ * If different slave configurations are pointing to the same slave during bus
+ * configuration, a warning is raised and only the first configuration is
+ * applied.
+ *
+ * This method allocates memory and should be called in non-realtime context
+ * before ecrt_master_activate().
+ *
+ * \retval >0 Pointer to the slave configuration structure.
+ * \retval NULL in the error case.
+ */
+ec_slave_config_t *ecrt_master_slave_config(
+ ec_master_t *master, /**< EtherCAT master */
+ uint16_t alias, /**< Slave alias. */
+ uint16_t position, /**< Slave position. */
+ uint32_t vendor_id, /**< Expected vendor ID. */
+ uint32_t product_code /**< Expected product code. */
+ );
+
+/** Selects the reference clock for distributed clocks.
+ *
+ * If this method is not called for a certain master, or if the slave
+ * configuration pointer is NULL, then the first slave with DC functionality
+ * will provide the reference clock.
+ *
+ * \return 0 on success, otherwise negative error code.
+ */
+int ecrt_master_select_reference_clock(
+ ec_master_t *master, /**< EtherCAT master. */
+ ec_slave_config_t *sc /**< Slave config of the slave to use as the
+ * reference slave (or NULL). */
+ );
+
+/** Obtains master information.
+ *
+ * No memory is allocated on the heap in
+ * this function.
+ *
+ * \attention The pointer to this structure must point to a valid variable.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ecrt_master(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_master_info_t *master_info /**< Structure that will output the
+ information */
+ );
+
+/** Obtains slave information.
+ *
+ * Tries to find the slave with the given ring position. The obtained
+ * information is stored in a structure. No memory is allocated on the heap in
+ * this function.
+ *
+ * \attention The pointer to this structure must point to a valid variable.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ecrt_master_get_slave(
+ ec_master_t *master, /**< EtherCAT master */
+ uint16_t slave_position, /**< Slave position. */
+ ec_slave_info_t *slave_info /**< Structure that will output the
+ information */
+ );
+
+#ifndef __KERNEL__
+
+/** Returns the proposed configuration of a slave's sync manager.
+ *
+ * Fills a given ec_sync_info_t structure with the attributes of a sync
+ * manager. The \a pdos field of the return value is left empty. Use
+ * ecrt_master_get_pdo() to get the PDO information.
+ *
+ * \return zero on success, else non-zero
+ */
+int ecrt_master_get_sync_manager(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint8_t sync_index, /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ ec_sync_info_t *sync /**< Pointer to output structure. */
+ );
+
+/** Returns information about a currently assigned PDO.
+ *
+ * Fills a given ec_pdo_info_t structure with the attributes of a currently
+ * assigned PDO of the given sync manager. The \a entries field of the return
+ * value is left empty. Use ecrt_master_get_pdo_entry() to get the PDO
+ * entry information.
+ *
+ * \retval zero on success, else non-zero
+ */
+int ecrt_master_get_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint8_t sync_index, /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ uint16_t pos, /**< Zero-based PDO position. */
+ ec_pdo_info_t *pdo /**< Pointer to output structure. */
+ );
+
+/** Returns information about a currently mapped PDO entry.
+ *
+ * Fills a given ec_pdo_entry_info_t structure with the attributes of a
+ * currently mapped PDO entry of the given PDO.
+ *
+ * \retval zero on success, else non-zero
+ */
+int ecrt_master_get_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint8_t sync_index, /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ uint16_t pdo_pos, /**< Zero-based PDO position. */
+ uint16_t entry_pos, /**< Zero-based PDO entry position. */
+ ec_pdo_entry_info_t *entry /**< Pointer to output structure. */
+ );
+
+#endif /* #ifndef __KERNEL__ */
+
+/** Executes an SDO download request to write data to a slave.
+ *
+ * This request is processed by the master state machine. This method blocks,
+ * until the request has been processed and may not be called in realtime
+ * context.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_sdo_download(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint16_t index, /**< Index of the SDO. */
+ uint8_t subindex, /**< Subindex of the SDO. */
+ uint8_t *data, /**< Data buffer to download. */
+ size_t data_size, /**< Size of the data buffer. */
+ uint32_t *abort_code /**< Abort code of the SDO download. */
+ );
+
+/** Executes an SDO download request to write data to a slave via complete
+ * access.
+ *
+ * This request is processed by the master state machine. This method blocks,
+ * until the request has been processed and may not be called in realtime
+ * context.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_sdo_download_complete(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint16_t index, /**< Index of the SDO. */
+ uint8_t *data, /**< Data buffer to download. */
+ size_t data_size, /**< Size of the data buffer. */
+ uint32_t *abort_code /**< Abort code of the SDO download. */
+ );
+
+/** Executes an SDO upload request to read data from a slave.
+ *
+ * This request is processed by the master state machine. This method blocks,
+ * until the request has been processed and may not be called in realtime
+ * context.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_sdo_upload(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint16_t index, /**< Index of the SDO. */
+ uint8_t subindex, /**< Subindex of the SDO. */
+ uint8_t *target, /**< Target buffer for the upload. */
+ size_t target_size, /**< Size of the target buffer. */
+ size_t *result_size, /**< Uploaded data size. */
+ uint32_t *abort_code /**< Abort code of the SDO upload. */
+ );
+
+/** Executes an SoE write request.
+ *
+ * Starts writing an IDN and blocks until the request was processed, or an
+ * error occurred.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_write_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint8_t drive_no, /**< Drive number. */
+ uint16_t idn, /**< SoE IDN (see ecrt_slave_config_idn()). */
+ uint8_t *data, /**< Pointer to data to write. */
+ size_t data_size, /**< Size of data to write. */
+ uint16_t *error_code /**< Pointer to variable, where an SoE error code
+ can be stored. */
+ );
+
+/** Executes an SoE read request.
+ *
+ * Starts reading an IDN and blocks until the request was processed, or an
+ * error occurred.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_read_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t slave_position, /**< Slave position. */
+ uint8_t drive_no, /**< Drive number. */
+ uint16_t idn, /**< SoE IDN (see ecrt_slave_config_idn()). */
+ uint8_t *target, /**< Pointer to memory where the read data can be
+ stored. */
+ size_t target_size, /**< Size of the memory \a target points to. */
+ size_t *result_size, /**< Actual size of the received data. */
+ uint16_t *error_code /**< Pointer to variable, where an SoE error code
+ can be stored. */
+ );
+
+/** Finishes the configuration phase and prepares for cyclic operation.
+ *
+ * This function tells the master that the configuration phase is finished and
+ * the realtime operation will begin. The function allocates internal memory
+ * for the domains and calculates the logical FMMU addresses for domain
+ * members. It tells the master state machine that the bus configuration is
+ * now to be applied.
+ *
+ * \attention After this function has been called, the realtime application is
+ * in charge of cyclically calling ecrt_master_send() and
+ * ecrt_master_receive() to ensure bus communication. Before calling this
+ * function, the master thread is responsible for that, so these functions may
+ * not be called! The method itself allocates memory and should not be called
+ * in realtime context.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ecrt_master_activate(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Deactivates the master.
+ *
+ * Removes the bus configuration. All objects created by
+ * ecrt_master_create_domain(), ecrt_master_slave_config(), ecrt_domain_data()
+ * ecrt_slave_config_create_sdo_request() and
+ * ecrt_slave_config_create_voe_handler() are freed, so pointers to them
+ * become invalid.
+ *
+ * This method should not be called in realtime context.
+ */
+void ecrt_master_deactivate(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Set interval between calls to ecrt_master_send().
+ *
+ * This information helps the master to decide, how much data can be appended
+ * to a frame by the master state machine. When the master is configured with
+ * --enable-hrtimers, this is used to calculate the scheduling of the master
+ * thread.
+ *
+ * \retval 0 on success.
+ * \retval <0 Error code.
+ */
+int ecrt_master_set_send_interval(
+ ec_master_t *master, /**< EtherCAT master. */
+ size_t send_interval /**< Send interval in us */
+ );
+
+/** Sends all datagrams in the queue.
+ *
+ * This method takes all datagrams, that have been queued for transmission,
+ * puts them into frames, and passes them to the Ethernet device for sending.
+ *
+ * Has to be called cyclically by the application after ecrt_master_activate()
+ * has returned.
+ */
+void ecrt_master_send(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Fetches received frames from the hardware and processes the datagrams.
+ *
+ * Queries the network device for received frames by calling the interrupt
+ * service routine. Extracts received datagrams and dispatches the results to
+ * the datagram objects in the queue. Received datagrams, and the ones that
+ * timed out, will be marked, and dequeued.
+ *
+ * Has to be called cyclically by the realtime application after
+ * ecrt_master_activate() has returned.
+ */
+void ecrt_master_receive(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Sends non-application datagrams.
+ *
+ * This method has to be called in the send callback function passed via
+ * ecrt_master_callbacks() to allow the sending of non-application datagrams.
+ */
+void ecrt_master_send_ext(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Reads the current master state.
+ *
+ * Stores the master state information in the given \a state structure.
+ *
+ * This method returns a global state. For the link-specific states in a
+ * redundant bus topology, use the ecrt_master_link_state() method.
+ */
+void ecrt_master_state(
+ const ec_master_t *master, /**< EtherCAT master. */
+ ec_master_state_t *state /**< Structure to store the information. */
+ );
+
+/** Reads the current state of a redundant link.
+ *
+ * Stores the link state information in the given \a state structure.
+ *
+ * \return Zero on success, otherwise negative error code.
+ */
+int ecrt_master_link_state(
+ const ec_master_t *master, /**< EtherCAT master. */
+ unsigned int dev_idx, /**< Index of the device (0 = main device, 1 =
+ first backup device, ...). */
+ ec_master_link_state_t *state /**< Structure to store the information.
+ */
+ );
+
+/** Sets the application time.
+ *
+ * The master has to know the application's time when operating slaves with
+ * distributed clocks. The time is not incremented by the master itself, so
+ * this method has to be called cyclically.
+ *
+ * \attention The time passed to this method is used to calculate the phase of
+ * the slaves' SYNC0/1 interrupts. It should be called constantly at the same
+ * point of the realtime cycle. So it is recommended to call it at the start
+ * of the calculations to avoid deviancies due to changing execution times.
+ *
+ * The time is used when setting the slaves' <tt>System Time Offset</tt> and
+ * <tt>Cyclic Operation Start Time</tt> registers and when synchronizing the
+ * DC reference clock to the application time via
+ * ecrt_master_sync_reference_clock().
+ *
+ * The time is defined as nanoseconds from 2000-01-01 00:00. Converting an
+ * epoch time can be done with the EC_TIMEVAL2NANO() macro, but is not
+ * necessary, since the absolute value is not of any interest.
+ */
+void ecrt_master_application_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint64_t app_time /**< Application time. */
+ );
+
+/** Queues the DC reference clock drift compensation datagram for sending.
+ *
+ * The reference clock will by synchronized to the application time provided
+ * by the last call off ecrt_master_application_time().
+ */
+void ecrt_master_sync_reference_clock(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Queues the DC reference clock drift compensation datagram for sending.
+ *
+ * The reference clock will by synchronized to the time passed in the
+ * sync_time parameter.
+ */
+void ecrt_master_sync_reference_clock_to(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint64_t sync_time /**< Sync reference clock to this time. */
+ );
+
+/** Queues the DC clock drift compensation datagram for sending.
+ *
+ * All slave clocks synchronized to the reference clock.
+ */
+void ecrt_master_sync_slave_clocks(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Get the lower 32 bit of the reference clock system time.
+ *
+ * This method can be used to synchronize the master to the reference clock.
+ *
+ * The reference clock system time is queried via the
+ * ecrt_master_sync_slave_clocks() method, that reads the system time of the
+ * reference clock and writes it to the slave clocks (so be sure to call it
+ * cyclically to get valid data).
+ *
+ * \attention The returned time is the system time of the reference clock
+ * minus the transmission delay of the reference clock.
+ *
+ * \retval 0 success, system time was written into \a time.
+ * \retval -ENXIO No reference clock found.
+ * \retval -EIO Slave synchronization datagram was not received.
+ */
+int ecrt_master_reference_clock_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint32_t *time /**< Pointer to store the queried system time. */
+ );
+
+/** Queues the DC synchrony monitoring datagram for sending.
+ *
+ * The datagram broadcast-reads all "System time difference" registers (\a
+ * 0x092c) to get an upper estimation of the DC synchrony. The result can be
+ * checked with the ecrt_master_sync_monitor_process() method.
+ */
+void ecrt_master_sync_monitor_queue(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Processes the DC synchrony monitoring datagram.
+ *
+ * If the sync monitoring datagram was sent before with
+ * ecrt_master_sync_monitor_queue(), the result can be queried with this
+ * method.
+ *
+ * \return Upper estimation of the maximum time difference in ns.
+ */
+uint32_t ecrt_master_sync_monitor_process(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/** Retry configuring slaves.
+ *
+ * Via this method, the application can tell the master to bring all slaves to
+ * OP state. In general, this is not necessary, because it is automatically
+ * done by the master. But with special slaves, that can be reconfigured by
+ * the vendor during runtime, it can be useful.
+ */
+void ecrt_master_reset(
+ ec_master_t *master /**< EtherCAT master. */
+ );
+
+/******************************************************************************
+ * Slave configuration methods
+ *****************************************************************************/
+
+/** Configure a sync manager.
+ *
+ * Sets the direction of a sync manager. This overrides the direction bits
+ * from the default control register from SII.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return zero on success, else non-zero
+ */
+int ecrt_slave_config_sync_manager(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t sync_index, /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ ec_direction_t direction, /**< Input/Output. */
+ ec_watchdog_mode_t watchdog_mode /** Watchdog mode. */
+ );
+
+/** Configure a slave's watchdog times.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ */
+void ecrt_slave_config_watchdog(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t watchdog_divider, /**< Number of 40 ns intervals. Used as a
+ base unit for all slave watchdogs. If set
+ to zero, the value is not written, so the
+ default is used. */
+ uint16_t watchdog_intervals /**< Number of base intervals for process
+ data watchdog. If set to zero, the value
+ is not written, so the default is used.
+ */
+ );
+
+/** Add a PDO to a sync manager's PDO assignment.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_pdos()
+ * \return zero on success, else non-zero
+ */
+int ecrt_slave_config_pdo_assign_add(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t sync_index, /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ uint16_t index /**< Index of the PDO to assign. */
+ );
+
+/** Clear a sync manager's PDO assignment.
+ *
+ * This can be called before assigning PDOs via
+ * ecrt_slave_config_pdo_assign_add(), to clear the default assignment of a
+ * sync manager.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_pdos()
+ */
+void ecrt_slave_config_pdo_assign_clear(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t sync_index /**< Sync manager index. Must be less
+ than #EC_MAX_SYNC_MANAGERS. */
+ );
+
+/** Add a PDO entry to the given PDO's mapping.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_pdos()
+ * \return zero on success, else non-zero
+ */
+int ecrt_slave_config_pdo_mapping_add(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t pdo_index, /**< Index of the PDO. */
+ uint16_t entry_index, /**< Index of the PDO entry to add to the PDO's
+ mapping. */
+ uint8_t entry_subindex, /**< Subindex of the PDO entry to add to the
+ PDO's mapping. */
+ uint8_t entry_bit_length /**< Size of the PDO entry in bit. */
+ );
+
+/** Clear the mapping of a given PDO.
+ *
+ * This can be called before mapping PDO entries via
+ * ecrt_slave_config_pdo_mapping_add(), to clear the default mapping.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_pdos()
+ */
+void ecrt_slave_config_pdo_mapping_clear(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t pdo_index /**< Index of the PDO. */
+ );
+
+/** Specify a complete PDO configuration.
+ *
+ * This function is a convenience wrapper for the functions
+ * ecrt_slave_config_sync_manager(), ecrt_slave_config_pdo_assign_clear(),
+ * ecrt_slave_config_pdo_assign_add(), ecrt_slave_config_pdo_mapping_clear()
+ * and ecrt_slave_config_pdo_mapping_add(), that are better suitable for
+ * automatic code generation.
+ *
+ * The following example shows, how to specify a complete configuration,
+ * including the PDO mappings. With this information, the master is able to
+ * reserve the complete process data, even if the slave is not present at
+ * configuration time:
+ *
+ * \code
+ * ec_pdo_entry_info_t el3162_channel1[] = {
+ * {0x3101, 1, 8}, // status
+ * {0x3101, 2, 16} // value
+ * };
+ *
+ * ec_pdo_entry_info_t el3162_channel2[] = {
+ * {0x3102, 1, 8}, // status
+ * {0x3102, 2, 16} // value
+ * };
+ *
+ * ec_pdo_info_t el3162_pdos[] = {
+ * {0x1A00, 2, el3162_channel1},
+ * {0x1A01, 2, el3162_channel2}
+ * };
+ *
+ * ec_sync_info_t el3162_syncs[] = {
+ * {2, EC_DIR_OUTPUT},
+ * {3, EC_DIR_INPUT, 2, el3162_pdos},
+ * {0xff}
+ * };
+ *
+ * if (ecrt_slave_config_pdos(sc_ana_in, EC_END, el3162_syncs)) {
+ * // handle error
+ * }
+ * \endcode
+ *
+ * The next example shows, how to configure the PDO assignment only. The
+ * entries for each assigned PDO are taken from the PDO's default mapping.
+ * Please note, that PDO entry registration will fail, if the PDO
+ * configuration is left empty and the slave is offline.
+ *
+ * \code
+ * ec_pdo_info_t pdos[] = {
+ * {0x1600}, // Channel 1
+ * {0x1601} // Channel 2
+ * };
+ *
+ * ec_sync_info_t syncs[] = {
+ * {3, EC_DIR_INPUT, 2, pdos},
+ * };
+ *
+ * if (ecrt_slave_config_pdos(slave_config_ana_in, 1, syncs)) {
+ * // handle error
+ * }
+ * \endcode
+ *
+ * Processing of \a syncs will stop, if
+ * - the number of processed items reaches \a n_syncs, or
+ * - the \a index member of an ec_sync_info_t item is 0xff. In this case,
+ * \a n_syncs should set to a number greater than the number of list items;
+ * using EC_END is recommended.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return zero on success, else non-zero
+ */
+int ecrt_slave_config_pdos(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int n_syncs, /**< Number of sync manager configurations in
+ \a syncs. */
+ const ec_sync_info_t syncs[] /**< Array of sync manager
+ configurations. */
+ );
+
+/** Registers a PDO entry for process data exchange in a domain.
+ *
+ * Searches the assigned PDOs for the given PDO entry. An error is raised, if
+ * the given entry is not mapped. Otherwise, the corresponding sync manager
+ * and FMMU configurations are provided for slave configuration and the
+ * respective sync manager's assigned PDOs are appended to the given domain,
+ * if not already done. The offset of the requested PDO entry's data inside
+ * the domain's process data is returned. Optionally, the PDO entry bit
+ * position (0-7) can be retrieved via the \a bit_position output parameter.
+ * This pointer may be \a NULL, in this case an error is raised if the PDO
+ * entry does not byte-align.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \retval >=0 Success: Offset of the PDO entry's process data.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_reg_pdo_entry(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t entry_index, /**< Index of the PDO entry to register. */
+ uint8_t entry_subindex, /**< Subindex of the PDO entry to register. */
+ ec_domain_t *domain, /**< Domain. */
+ unsigned int *bit_position /**< Optional address if bit addressing
+ is desired */
+ );
+
+/** Registers a PDO entry using its position.
+ *
+ * Similar to ecrt_slave_config_reg_pdo_entry(), but not using PDO indices but
+ * offsets in the PDO mapping, because PDO entry indices may not be unique
+ * inside a slave's PDO mapping. An error is raised, if
+ * one of the given positions is out of range.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \retval >=0 Success: Offset of the PDO entry's process data.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_reg_pdo_entry_pos(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t sync_index, /**< Sync manager index. */
+ unsigned int pdo_pos, /**< Position of the PDO inside the SM. */
+ unsigned int entry_pos, /**< Position of the entry inside the PDO. */
+ ec_domain_t *domain, /**< Domain. */
+ unsigned int *bit_position /**< Optional address if bit addressing
+ is desired */
+ );
+
+/** Configure distributed clocks.
+ *
+ * Sets the AssignActivate word and the cycle and shift times for the sync
+ * signals.
+ *
+ * The AssignActivate word is vendor-specific and can be taken from the XML
+ * device description file (Device -> Dc -> AssignActivate). Set this to zero,
+ * if the slave shall be operated without distributed clocks (default).
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \attention The \a sync1_shift time is ignored.
+ */
+void ecrt_slave_config_dc(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t assign_activate, /**< AssignActivate word. */
+ uint32_t sync0_cycle, /**< SYNC0 cycle time [ns]. */
+ int32_t sync0_shift, /**< SYNC0 shift time [ns]. */
+ uint32_t sync1_cycle, /**< SYNC1 cycle time [ns]. */
+ int32_t sync1_shift /**< SYNC1 shift time [ns]. */
+ );
+
+/** Add an SDO configuration.
+ *
+ * An SDO configuration is stored in the slave configuration object and is
+ * downloaded to the slave whenever the slave is being configured by the
+ * master. This usually happens once on master activation, but can be repeated
+ * subsequently, for example after the slave's power supply failed.
+ *
+ * \attention The SDOs for PDO assignment (\p 0x1C10 - \p 0x1C2F) and PDO
+ * mapping (\p 0x1600 - \p 0x17FF and \p 0x1A00 - \p 0x1BFF) should not be
+ * configured with this function, because they are part of the slave
+ * configuration done by the master. Please use ecrt_slave_config_pdos() and
+ * friends instead.
+ *
+ * This is the generic function for adding an SDO configuration. Please note
+ * that the this function does not do any endianness correction. If
+ * datatype-specific functions are needed (that automatically correct the
+ * endianness), have a look at ecrt_slave_config_sdo8(),
+ * ecrt_slave_config_sdo16() and ecrt_slave_config_sdo32().
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_sdo(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t index, /**< Index of the SDO to configure. */
+ uint8_t subindex, /**< Subindex of the SDO to configure. */
+ const uint8_t *data, /**< Pointer to the data. */
+ size_t size /**< Size of the \a data. */
+ );
+
+/** Add a configuration value for an 8-bit SDO.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_sdo().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_sdo8(
+ ec_slave_config_t *sc, /**< Slave configuration */
+ uint16_t sdo_index, /**< Index of the SDO to configure. */
+ uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
+ uint8_t value /**< Value to set. */
+ );
+
+/** Add a configuration value for a 16-bit SDO.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_sdo().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_sdo16(
+ ec_slave_config_t *sc, /**< Slave configuration */
+ uint16_t sdo_index, /**< Index of the SDO to configure. */
+ uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
+ uint16_t value /**< Value to set. */
+ );
+
+/** Add a configuration value for a 32-bit SDO.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_sdo().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_sdo32(
+ ec_slave_config_t *sc, /**< Slave configuration */
+ uint16_t sdo_index, /**< Index of the SDO to configure. */
+ uint8_t sdo_subindex, /**< Subindex of the SDO to configure. */
+ uint32_t value /**< Value to set. */
+ );
+
+/** Add configuration data for a complete SDO.
+ *
+ * The SDO data are transferred via CompleteAccess. Data for the first
+ * subindex (0) have to be included.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_sdo().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_complete_sdo(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t index, /**< Index of the SDO to configure. */
+ const uint8_t *data, /**< Pointer to the data. */
+ size_t size /**< Size of the \a data. */
+ );
+
+/** Set the size of the CoE emergency ring buffer.
+ *
+ * The initial size is zero, so all messages will be dropped. This method can
+ * be called even after master activation, but it will clear the ring buffer!
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return 0 on success, or negative error code.
+ */
+int ecrt_slave_config_emerg_size(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ size_t elements /**< Number of records of the CoE emergency ring. */
+ );
+
+/** Read and remove one record from the CoE emergency ring buffer.
+ *
+ * A record consists of 8 bytes:
+ *
+ * Byte 0-1: Error code (little endian)
+ * Byte 2: Error register
+ * Byte 3-7: Data
+ *
+ * \return 0 on success (record popped), or negative error code (i. e.
+ * -ENOENT, if ring is empty).
+ */
+int ecrt_slave_config_emerg_pop(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t *target /**< Pointer to target memory (at least
+ EC_COE_EMERGENCY_MSG_SIZE bytes). */
+ );
+
+/** Clears CoE emergency ring buffer and the overrun counter.
+ *
+ * \return 0 on success, or negative error code.
+ */
+int ecrt_slave_config_emerg_clear(
+ ec_slave_config_t *sc /**< Slave configuration. */
+ );
+
+/** Read the number of CoE emergency overruns.
+ *
+ * The overrun counter will be incremented when a CoE emergency message could
+ * not be stored in the ring buffer and had to be dropped. Call
+ * ecrt_slave_config_emerg_clear() to reset the counter.
+ *
+ * \return Number of overruns since last clear, or negative error code.
+ */
+int ecrt_slave_config_emerg_overruns(
+ ec_slave_config_t *sc /**< Slave configuration. */
+ );
+
+/** Create an SDO request to exchange SDOs during realtime operation.
+ *
+ * The created SDO request object is freed automatically when the master is
+ * released.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return New SDO request, or NULL on error.
+ */
+ec_sdo_request_t *ecrt_slave_config_create_sdo_request(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint16_t index, /**< SDO index. */
+ uint8_t subindex, /**< SDO subindex. */
+ size_t size /**< Data size to reserve. */
+ );
+
+/** Create an VoE handler to exchange vendor-specific data during realtime
+ * operation.
+ *
+ * The number of VoE handlers per slave configuration is not limited, but
+ * usually it is enough to create one for sending and one for receiving, if
+ * both can be done simultaneously.
+ *
+ * The created VoE handler object is freed automatically when the master is
+ * released.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return New VoE handler, or NULL on error.
+ */
+ec_voe_handler_t *ecrt_slave_config_create_voe_handler(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ size_t size /**< Data size to reserve. */
+ );
+
+/** Create a register request to exchange EtherCAT register contents during
+ * realtime operation.
+ *
+ * This interface should not be used to take over master functionality,
+ * instead it is intended for debugging and monitoring reasons.
+ *
+ * The created register request object is freed automatically when the master
+ * is released.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \return New register request, or NULL on error.
+ */
+ec_reg_request_t *ecrt_slave_config_create_reg_request(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ size_t size /**< Data size to reserve. */
+ );
+
+/** Outputs the state of the slave configuration.
+ *
+ * Stores the state information in the given \a state structure. The state
+ * information is updated by the master state machine, so it may take a few
+ * cycles, until it changes.
+ *
+ * \attention If the state of process data exchange shall be monitored in
+ * realtime, ecrt_domain_state() should be used.
+ */
+void ecrt_slave_config_state(
+ const ec_slave_config_t *sc, /**< Slave configuration */
+ ec_slave_config_state_t *state /**< State object to write to. */
+ );
+
+/** Add an SoE IDN configuration.
+ *
+ * A configuration for a Sercos-over-EtherCAT IDN is stored in the slave
+ * configuration object and is written to the slave whenever the slave is
+ * being configured by the master. This usually happens once on master
+ * activation, but can be repeated subsequently, for example after the slave's
+ * power supply failed.
+ *
+ * The \a idn parameter can be separated into several sections:
+ * - Bit 15: Standard data (0) or Product data (1)
+ * - Bit 14 - 12: Parameter set (0 - 7)
+ * - Bit 11 - 0: Data block number (0 - 4095)
+ *
+ * Please note that the this function does not do any endianness correction.
+ * Multi-byte data have to be passed in EtherCAT endianness (little-endian).
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ecrt_slave_config_idn(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ uint8_t drive_no, /**< Drive number. */
+ uint16_t idn, /**< SoE IDN. */
+ ec_al_state_t state, /**< AL state in which to write the IDN (PREOP or
+ SAFEOP). */
+ const uint8_t *data, /**< Pointer to the data. */
+ size_t size /**< Size of the \a data. */
+ );
+
+/******************************************************************************
+ * Domain methods
+ *****************************************************************************/
+
+/** Registers a bunch of PDO entries for a domain.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ * \see ecrt_slave_config_reg_pdo_entry()
+ *
+ * \attention The registration array has to be terminated with an empty
+ * structure, or one with the \a index field set to zero!
+ * \return 0 on success, else non-zero.
+ */
+int ecrt_domain_reg_pdo_entry_list(
+ ec_domain_t *domain, /**< Domain. */
+ const ec_pdo_entry_reg_t *pdo_entry_regs /**< Array of PDO
+ registrations. */
+ );
+
+/** Returns the current size of the domain's process data.
+ *
+ * \return Size of the process data image, or a negative error code.
+ */
+size_t ecrt_domain_size(
+ const ec_domain_t *domain /**< Domain. */
+ );
+
+#ifdef __KERNEL__
+
+/** Provide external memory to store the domain's process data.
+ *
+ * Call this after all PDO entries have been registered and before activating
+ * the master.
+ *
+ * The size of the allocated memory must be at least ecrt_domain_size(), after
+ * all PDO entries have been registered.
+ *
+ * This method has to be called in non-realtime context before
+ * ecrt_master_activate().
+ *
+ */
+void ecrt_domain_external_memory(
+ ec_domain_t *domain, /**< Domain. */
+ uint8_t *memory /**< Address of the memory to store the process
+ data in. */
+ );
+
+#endif /* __KERNEL__ */
+
+/** Returns the domain's process data.
+ *
+ * - In kernel context: If external memory was provided with
+ * ecrt_domain_external_memory(), the returned pointer will contain the
+ * address of that memory. Otherwise it will point to the internally allocated
+ * memory. In the latter case, this method may not be called before
+ * ecrt_master_activate().
+ *
+ * - In userspace context: This method has to be called after
+ * ecrt_master_activate() to get the mapped domain process data memory.
+ *
+ * \return Pointer to the process data memory.
+ */
+uint8_t *ecrt_domain_data(
+ ec_domain_t *domain /**< Domain. */
+ );
+
+/** Determines the states of the domain's datagrams.
+ *
+ * Evaluates the working counters of the received datagrams and outputs
+ * statistics, if necessary. This must be called after ecrt_master_receive()
+ * is expected to receive the domain datagrams in order to make
+ * ecrt_domain_state() return the result of the last process data exchange.
+ */
+void ecrt_domain_process(
+ ec_domain_t *domain /**< Domain. */
+ );
+
+/** (Re-)queues all domain datagrams in the master's datagram queue.
+ *
+ * Call this function to mark the domain's datagrams for exchanging at the
+ * next call of ecrt_master_send().
+ */
+void ecrt_domain_queue(
+ ec_domain_t *domain /**< Domain. */
+ );
+
+/** Reads the state of a domain.
+ *
+ * Stores the domain state in the given \a state structure.
+ *
+ * Using this method, the process data exchange can be monitored in realtime.
+ */
+void ecrt_domain_state(
+ const ec_domain_t *domain, /**< Domain. */
+ ec_domain_state_t *state /**< Pointer to a state object to store the
+ information. */
+ );
+
+/*****************************************************************************
+ * SDO request methods.
+ ****************************************************************************/
+
+/** Set the SDO index and subindex.
+ *
+ * \attention If the SDO index and/or subindex is changed while
+ * ecrt_sdo_request_state() returns EC_REQUEST_BUSY, this may lead to
+ * unexpected results.
+ */
+void ecrt_sdo_request_index(
+ ec_sdo_request_t *req, /**< SDO request. */
+ uint16_t index, /**< SDO index. */
+ uint8_t subindex /**< SDO subindex. */
+ );
+
+/** Set the timeout for an SDO request.
+ *
+ * If the request cannot be processed in the specified time, if will be marked
+ * as failed.
+ *
+ * The timeout is permanently stored in the request object and is valid until
+ * the next call of this method.
+ */
+void ecrt_sdo_request_timeout(
+ ec_sdo_request_t *req, /**< SDO request. */
+ uint32_t timeout /**< Timeout in milliseconds. Zero means no
+ timeout. */
+ );
+
+/** Access to the SDO request's data.
+ *
+ * This function returns a pointer to the request's internal SDO data memory.
+ *
+ * - After a read operation was successful, integer data can be evaluated using
+ * the EC_READ_*() macros as usual. Example:
+ * \code
+ * uint16_t value = EC_READ_U16(ecrt_sdo_request_data(sdo)));
+ * \endcode
+ * - If a write operation shall be triggered, the data have to be written to
+ * the internal memory. Use the EC_WRITE_*() macros, if you are writing
+ * integer data. Be sure, that the data fit into the memory. The memory size
+ * is a parameter of ecrt_slave_config_create_sdo_request().
+ * \code
+ * EC_WRITE_U16(ecrt_sdo_request_data(sdo), 0xFFFF);
+ * \endcode
+ *
+ * \attention The return value can be invalid during a read operation, because
+ * the internal SDO data memory could be re-allocated if the read SDO data do
+ * not fit inside.
+ *
+ * \return Pointer to the internal SDO data memory.
+ */
+uint8_t *ecrt_sdo_request_data(
+ ec_sdo_request_t *req /**< SDO request. */
+ );
+
+/** Returns the current SDO data size.
+ *
+ * When the SDO request is created, the data size is set to the size of the
+ * reserved memory. After a read operation the size is set to the size of the
+ * read data. The size is not modified in any other situation.
+ *
+ * \return SDO data size in bytes.
+ */
+size_t ecrt_sdo_request_data_size(
+ const ec_sdo_request_t *req /**< SDO request. */
+ );
+
+/** Get the current state of the SDO request.
+ *
+ * \return Request state.
+ */
+#ifdef __KERNEL__
+ec_request_state_t ecrt_sdo_request_state(
+ const ec_sdo_request_t *req /**< SDO request. */
+ );
+#else
+ec_request_state_t ecrt_sdo_request_state(
+ ec_sdo_request_t *req /**< SDO request. */
+ );
+#endif
+
+/** Schedule an SDO write operation.
+ *
+ * \attention This method may not be called while ecrt_sdo_request_state()
+ * returns EC_REQUEST_BUSY.
+ */
+void ecrt_sdo_request_write(
+ ec_sdo_request_t *req /**< SDO request. */
+ );
+
+/** Schedule an SDO read operation.
+ *
+ * \attention This method may not be called while ecrt_sdo_request_state()
+ * returns EC_REQUEST_BUSY.
+ *
+ * \attention After calling this function, the return value of
+ * ecrt_sdo_request_data() must be considered as invalid while
+ * ecrt_sdo_request_state() returns EC_REQUEST_BUSY.
+ */
+void ecrt_sdo_request_read(
+ ec_sdo_request_t *req /**< SDO request. */
+ );
+
+/*****************************************************************************
+ * VoE handler methods.
+ ****************************************************************************/
+
+/** Sets the VoE header for future send operations.
+ *
+ * A VoE message shall contain a 4-byte vendor ID, followed by a 2-byte vendor
+ * type at as header. These numbers can be set with this function. The values
+ * are valid and will be used for future send operations until the next call
+ * of this method.
+ */
+void ecrt_voe_handler_send_header(
+ ec_voe_handler_t *voe, /**< VoE handler. */
+ uint32_t vendor_id, /**< Vendor ID. */
+ uint16_t vendor_type /**< Vendor-specific type. */
+ );
+
+/** Reads the header data of a received VoE message.
+ *
+ * This method can be used to get the received VoE header information after a
+ * read operation has succeeded.
+ *
+ * The header information is stored at the memory given by the pointer
+ * parameters.
+ */
+void ecrt_voe_handler_received_header(
+ const ec_voe_handler_t *voe, /**< VoE handler. */
+ uint32_t *vendor_id, /**< Vendor ID. */
+ uint16_t *vendor_type /**< Vendor-specific type. */
+ );
+
+/** Access to the VoE handler's data.
+ *
+ * This function returns a pointer to the VoE handler's internal memory, that
+ * points to the actual VoE data right after the VoE header (see
+ * ecrt_voe_handler_send_header()).
+ *
+ * - After a read operation was successful, the memory contains the received
+ * data. The size of the received data can be determined via
+ * ecrt_voe_handler_data_size().
+ * - Before a write operation is triggered, the data have to be written to the
+ * internal memory. Be sure, that the data fit into the memory. The reserved
+ * memory size is a parameter of ecrt_slave_config_create_voe_handler().
+ *
+ * \attention The returned pointer is not necessarily persistent: After a read
+ * operation, the internal memory may have been reallocated. This can be
+ * avoided by reserving enough memory via the \a size parameter of
+ * ecrt_slave_config_create_voe_handler().
+ *
+ * \return Pointer to the internal memory.
+ */
+uint8_t *ecrt_voe_handler_data(
+ ec_voe_handler_t *voe /**< VoE handler. */
+ );
+
+/** Returns the current data size.
+ *
+ * The data size is the size of the VoE data without the header (see
+ * ecrt_voe_handler_send_header()).
+ *
+ * When the VoE handler is created, the data size is set to the size of the
+ * reserved memory. At a write operation, the data size is set to the number
+ * of bytes to write. After a read operation the size is set to the size of
+ * the read data. The size is not modified in any other situation.
+ *
+ * \return Data size in bytes.
+ */
+size_t ecrt_voe_handler_data_size(
+ const ec_voe_handler_t *voe /**< VoE handler. */
+ );
+
+/** Start a VoE write operation.
+ *
+ * After this function has been called, the ecrt_voe_handler_execute() method
+ * must be called in every bus cycle as long as it returns EC_REQUEST_BUSY. No
+ * other operation may be started while the handler is busy.
+ */
+void ecrt_voe_handler_write(
+ ec_voe_handler_t *voe, /**< VoE handler. */
+ size_t size /**< Number of bytes to write (without the VoE header). */
+ );
+
+/** Start a VoE read operation.
+ *
+ * After this function has been called, the ecrt_voe_handler_execute() method
+ * must be called in every bus cycle as long as it returns EC_REQUEST_BUSY. No
+ * other operation may be started while the handler is busy.
+ *
+ * The state machine queries the slave's send mailbox for new data to be send
+ * to the master. If no data appear within the EC_VOE_RESPONSE_TIMEOUT
+ * (defined in master/voe_handler.c), the operation fails.
+ *
+ * On success, the size of the read data can be determined via
+ * ecrt_voe_handler_data_size(), while the VoE header of the received data
+ * can be retrieved with ecrt_voe_handler_received_header().
+ */
+void ecrt_voe_handler_read(
+ ec_voe_handler_t *voe /**< VoE handler. */
+ );
+
+/** Start a VoE read operation without querying the sync manager status.
+ *
+ * After this function has been called, the ecrt_voe_handler_execute() method
+ * must be called in every bus cycle as long as it returns EC_REQUEST_BUSY. No
+ * other operation may be started while the handler is busy.
+ *
+ * The state machine queries the slave by sending an empty mailbox. The slave
+ * fills its data to the master in this mailbox. If no data appear within the
+ * EC_VOE_RESPONSE_TIMEOUT (defined in master/voe_handler.c), the operation
+ * fails.
+ *
+ * On success, the size of the read data can be determined via
+ * ecrt_voe_handler_data_size(), while the VoE header of the received data
+ * can be retrieved with ecrt_voe_handler_received_header().
+ */
+void ecrt_voe_handler_read_nosync(
+ ec_voe_handler_t *voe /**< VoE handler. */
+ );
+
+/** Execute the handler.
+ *
+ * This method executes the VoE handler. It has to be called in every bus
+ * cycle as long as it returns EC_REQUEST_BUSY.
+ *
+ * \return Handler state.
+ */
+ec_request_state_t ecrt_voe_handler_execute(
+ ec_voe_handler_t *voe /**< VoE handler. */
+ );
+
+/*****************************************************************************
+ * Register request methods.
+ ****************************************************************************/
+
+/** Access to the register request's data.
+ *
+ * This function returns a pointer to the request's internal memory.
+ *
+ * - After a read operation was successful, integer data can be evaluated
+ * using the EC_READ_*() macros as usual. Example:
+ * \code
+ * uint16_t value = EC_READ_U16(ecrt_reg_request_data(reg_request)));
+ * \endcode
+ * - If a write operation shall be triggered, the data have to be written to
+ * the internal memory. Use the EC_WRITE_*() macros, if you are writing
+ * integer data. Be sure, that the data fit into the memory. The memory size
+ * is a parameter of ecrt_slave_config_create_reg_request().
+ * \code
+ * EC_WRITE_U16(ecrt_reg_request_data(reg_request), 0xFFFF);
+ * \endcode
+ *
+ * \return Pointer to the internal memory.
+ */
+uint8_t *ecrt_reg_request_data(
+ ec_reg_request_t *req /**< Register request. */
+ );
+
+/** Get the current state of the register request.
+ *
+ * \return Request state.
+ */
+#ifdef __KERNEL__
+ec_request_state_t ecrt_reg_request_state(
+ const ec_reg_request_t *req /**< Register request. */
+ );
+#else
+ec_request_state_t ecrt_reg_request_state(
+ ec_reg_request_t *req /**< Register request. */
+ );
+#endif
+
+/** Schedule an register write operation.
+ *
+ * \attention This method may not be called while ecrt_reg_request_state()
+ * returns EC_REQUEST_BUSY.
+ *
+ * \attention The \a size parameter is truncated to the size given at request
+ * creation.
+ */
+void ecrt_reg_request_write(
+ ec_reg_request_t *req, /**< Register request. */
+ uint16_t address, /**< Register address. */
+ size_t size /**< Size to write. */
+ );
+
+/** Schedule a register read operation.
+ *
+ * \attention This method may not be called while ecrt_reg_request_state()
+ * returns EC_REQUEST_BUSY.
+ *
+ * \attention The \a size parameter is truncated to the size given at request
+ * creation.
+ */
+void ecrt_reg_request_read(
+ ec_reg_request_t *req, /**< Register request. */
+ uint16_t address, /**< Register address. */
+ size_t size /**< Size to write. */
+ );
+
+/******************************************************************************
+ * Bitwise read/write macros
+ *****************************************************************************/
+
+/** Read a certain bit of an EtherCAT data byte.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param POS bit position
+ */
+#define EC_READ_BIT(DATA, POS) ((*((uint8_t *) (DATA)) >> (POS)) & 0x01)
+
+/** Write a certain bit of an EtherCAT data byte.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param POS bit position
+ * \param VAL new bit value
+ */
+#define EC_WRITE_BIT(DATA, POS, VAL) \
+ do { \
+ if (VAL) *((uint8_t *) (DATA)) |= (1 << (POS)); \
+ else *((uint8_t *) (DATA)) &= ~(1 << (POS)); \
+ } while (0)
+
+/******************************************************************************
+ * Byte-swapping functions for user space
+ *****************************************************************************/
+
+#ifndef __KERNEL__
+
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+
+#define le16_to_cpu(x) x
+#define le32_to_cpu(x) x
+#define le64_to_cpu(x) x
+
+#define cpu_to_le16(x) x
+#define cpu_to_le32(x) x
+#define cpu_to_le64(x) x
+
+#elif __BYTE_ORDER == __BIG_ENDIAN
+
+#define swap16(x) \
+ ((uint16_t)( \
+ (((uint16_t)(x) & 0x00ffU) << 8) | \
+ (((uint16_t)(x) & 0xff00U) >> 8) ))
+#define swap32(x) \
+ ((uint32_t)( \
+ (((uint32_t)(x) & 0x000000ffUL) << 24) | \
+ (((uint32_t)(x) & 0x0000ff00UL) << 8) | \
+ (((uint32_t)(x) & 0x00ff0000UL) >> 8) | \
+ (((uint32_t)(x) & 0xff000000UL) >> 24) ))
+#define swap64(x) \
+ ((uint64_t)( \
+ (((uint64_t)(x) & 0x00000000000000ffULL) << 56) | \
+ (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \
+ (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \
+ (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \
+ (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \
+ (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \
+ (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \
+ (((uint64_t)(x) & 0xff00000000000000ULL) >> 56) ))
+
+#define le16_to_cpu(x) swap16(x)
+#define le32_to_cpu(x) swap32(x)
+#define le64_to_cpu(x) swap64(x)
+
+#define cpu_to_le16(x) swap16(x)
+#define cpu_to_le32(x) swap32(x)
+#define cpu_to_le64(x) swap64(x)
+
+#endif
+
+#define le16_to_cpup(x) le16_to_cpu(*((uint16_t *)(x)))
+#define le32_to_cpup(x) le32_to_cpu(*((uint32_t *)(x)))
+#define le64_to_cpup(x) le64_to_cpu(*((uint64_t *)(x)))
+
+#endif /* ifndef __KERNEL__ */
+
+/******************************************************************************
+ * Read macros
+ *****************************************************************************/
+
+/** Read an 8-bit unsigned value from EtherCAT data.
+ *
+ * \return EtherCAT data value
+ */
+#define EC_READ_U8(DATA) \
+ ((uint8_t) *((uint8_t *) (DATA)))
+
+/** Read an 8-bit signed value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_S8(DATA) \
+ ((int8_t) *((uint8_t *) (DATA)))
+
+/** Read a 16-bit unsigned value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_U16(DATA) \
+ ((uint16_t) le16_to_cpup((void *) (DATA)))
+
+/** Read a 16-bit signed value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_S16(DATA) \
+ ((int16_t) le16_to_cpup((void *) (DATA)))
+
+/** Read a 32-bit unsigned value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_U32(DATA) \
+ ((uint32_t) le32_to_cpup((void *) (DATA)))
+
+/** Read a 32-bit signed value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_S32(DATA) \
+ ((int32_t) le32_to_cpup((void *) (DATA)))
+
+/** Read a 64-bit unsigned value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_U64(DATA) \
+ ((uint64_t) le64_to_cpup((void *) (DATA)))
+
+/** Read a 64-bit signed value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_S64(DATA) \
+ ((int64_t) le64_to_cpup((void *) (DATA)))
+
+/******************************************************************************
+ * Floating-point read functions and macros (userspace only)
+ *****************************************************************************/
+
+#ifndef __KERNEL__
+
+/** Read a 32-bit floating-point value from EtherCAT data.
+ *
+ * \param data EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+float ecrt_read_real(const void *data);
+
+/** Read a 32-bit floating-point value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_REAL(DATA) ecrt_read_real(DATA)
+
+/** Read a 64-bit floating-point value from EtherCAT data.
+ *
+ * \param data EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+double ecrt_read_lreal(const void *data);
+
+/** Read a 64-bit floating-point value from EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \return EtherCAT data value
+ */
+#define EC_READ_LREAL(DATA) ecrt_read_lreal(DATA)
+
+#endif // ifndef __KERNEL__
+
+/******************************************************************************
+ * Write macros
+ *****************************************************************************/
+
+/** Write an 8-bit unsigned value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_U8(DATA, VAL) \
+ do { \
+ *((uint8_t *)(DATA)) = ((uint8_t) (VAL)); \
+ } while (0)
+
+/** Write an 8-bit signed value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_S8(DATA, VAL) EC_WRITE_U8(DATA, VAL)
+
+/** Write a 16-bit unsigned value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_U16(DATA, VAL) \
+ do { \
+ *((uint16_t *) (DATA)) = cpu_to_le16((uint16_t) (VAL)); \
+ } while (0)
+
+/** Write a 16-bit signed value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_S16(DATA, VAL) EC_WRITE_U16(DATA, VAL)
+
+/** Write a 32-bit unsigned value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_U32(DATA, VAL) \
+ do { \
+ *((uint32_t *) (DATA)) = cpu_to_le32((uint32_t) (VAL)); \
+ } while (0)
+
+/** Write a 32-bit signed value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_S32(DATA, VAL) EC_WRITE_U32(DATA, VAL)
+
+/** Write a 64-bit unsigned value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_U64(DATA, VAL) \
+ do { \
+ *((uint64_t *) (DATA)) = cpu_to_le64((uint64_t) (VAL)); \
+ } while (0)
+
+/** Write a 64-bit signed value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_S64(DATA, VAL) EC_WRITE_U64(DATA, VAL)
+
+/******************************************************************************
+ * Floating-point write functions and macros (userspace only)
+ *****************************************************************************/
+
+#ifndef __KERNEL__
+
+/** Write a 32-bit floating-point value to EtherCAT data.
+ *
+ * \param data EtherCAT data pointer
+ * \param value new value
+ */
+void ecrt_write_real(void *data, float value);
+
+/** Write a 32-bit floating-point value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_REAL(DATA, VAL) ecrt_write_real(DATA, VAL)
+
+/** Write a 64-bit floating-point value to EtherCAT data.
+ *
+ * \param data EtherCAT data pointer
+ * \param value new value
+ */
+void ecrt_write_lreal(void *data, double value);
+
+/** Write a 64-bit floating-point value to EtherCAT data.
+ *
+ * \param DATA EtherCAT data pointer
+ * \param VAL new value
+ */
+#define EC_WRITE_LREAL(DATA, VAL) ecrt_write_lreal(DATA, VAL)
+
+#endif // ifndef __KERNEL__
+
+/*****************************************************************************/
+
+#ifdef __cplusplus
+}
+#endif
+
+/*****************************************************************************/
+
+/** @} */
+
+#endif
diff --git a/net/ethercat/include/ectty.h b/net/ethercat/include/ectty.h
new file mode 100644
index 000000000000..4b45d96ec52a
--- /dev/null
+++ b/net/ethercat/include/ectty.h
@@ -0,0 +1,114 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ * This file is part of the IgH EtherCAT master userspace library.
+ *
+ * The IgH EtherCAT master userspace library is free software; you can
+ * redistribute it and/or modify it under the terms of the GNU Lesser General
+ * Public License as published by the Free Software Foundation; version 2.1
+ * of the License.
+ *
+ * The IgH EtherCAT master userspace library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with the IgH EtherCAT master userspace library. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * ---
+ *
+ * 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 virtual TTY interface.
+ *
+ * \defgroup TTYInterface EtherCAT Virtual TTY Interface
+ *
+ * @{
+ */
+
+/*****************************************************************************/
+
+#ifndef __ECTTY_H__
+#define __ECTTY_H__
+
+#include <linux/termios.h>
+
+/******************************************************************************
+ * Data types
+ *****************************************************************************/
+
+struct ec_tty;
+typedef struct ec_tty ec_tty_t; /**< \see ec_tty */
+
+/** Operations on the virtual TTY interface.
+ */
+typedef struct {
+ int (*cflag_changed)(void *, tcflag_t); /**< Called when the serial
+ * settings shall be changed. The
+ * \a cflag argument contains the
+ * new settings. */
+} ec_tty_operations_t;
+
+/******************************************************************************
+ * Global functions
+ *****************************************************************************/
+
+/** Create a virtual TTY interface.
+ *
+ * \param ops Set of callbacks.
+ * \param cb_data Arbitrary data, that is passed to any callback.
+ *
+ * \return Pointer to the interface object, otherwise an ERR_PTR value.
+ */
+ec_tty_t *ectty_create(
+ const ec_tty_operations_t *ops,
+ void *cb_data
+ );
+
+/******************************************************************************
+ * TTY interface methods
+ *****************************************************************************/
+
+/** Releases a virtual TTY interface.
+ */
+void ectty_free(
+ ec_tty_t *tty /**< TTY interface. */
+ );
+
+/** Reads data to send from the TTY interface.
+ *
+ * If there are data to send, they are copied into the \a buffer. At maximum,
+ * \a size bytes are copied. The actual number of bytes copied is returned.
+ *
+ * \return Number of bytes copied.
+ */
+unsigned int ectty_tx_data(
+ ec_tty_t *tty, /**< TTY interface. */
+ uint8_t *buffer, /**< Buffer for data to transmit. */
+ size_t size /**< Available space in \a buffer. */
+ );
+
+/** Pushes received data to the TTY interface.
+ */
+void ectty_rx_data(
+ ec_tty_t *tty, /**< TTY interface. */
+ const uint8_t *buffer, /**< Buffer with received data. */
+ size_t size /**< Number of bytes in \a buffer. */
+ );
+
+/*****************************************************************************/
+
+/** @} */
+
+#endif
diff --git a/net/ethercat/master/Kconfig b/net/ethercat/master/Kconfig
new file mode 100644
index 000000000000..92482be5cb35
--- /dev/null
+++ b/net/ethercat/master/Kconfig
@@ -0,0 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ethercat
+#
+
+config ETHERCAT_MASTER
+ tristate "ethercat master"
+ default n
+ help
+ Support for ethercat master driver.
+ If unsure, say Y.
+
+config ETHERCAT_RTDM
+ bool "ethercat rtdm"
+ depends on ETHERCAT_MASTER
+ default n
+ help
+ Support for ethercat master driver.
+ If unsure, say Y.
diff --git a/net/ethercat/master/Makefile b/net/ethercat/master/Makefile
new file mode 100644
index 000000000000..dff96f52e2d8
--- /dev/null
+++ b/net/ethercat/master/Makefile
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the ethercat driver.
+#
+
+obj-$(CONFIG_ETHERCAT_MASTER) += ethercat_master.o
+ethercat_master-y += cdev.o coe_emerg_ring.o datagram.o datagram_pair.o debug.o device.o \
+ domain.o doxygen.o ethernet.o fmmu_config.o foe_request.o fsm_change.o \
+ fsm_coe.o fsm_foe.o fsm_master.o fsm_pdo.o fsm_pdo_entry.o fsm_sii.o \
+ fsm_slave.o fsm_slave_config.o fsm_slave_scan.o fsm_soe.o ioctl.o \
+ mailbox.o master.o module.o pdo.o pdo_entry.o pdo_list.o reg_request.o \
+ sdo.o sdo_entry.o sdo_request.o slave.o slave_config.o \
+ soe_errors.o soe_request.o sync.o sync_config.o voe_handler.o
+ethercat_master-$(CONFIG_ETHERCAT_RTDM) += rtdm.o rtdm-ioctl.o
diff --git a/net/ethercat/master/cdev.c b/net/ethercat/master/cdev.c
new file mode 100644
index 000000000000..5bba2be061f7
--- /dev/null
+++ b/net/ethercat/master/cdev.c
@@ -0,0 +1,333 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2006-2020 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 master character device.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+
+#include "cdev.h"
+#include "master.h"
+#include "slave_config.h"
+#include "voe_handler.h"
+#include "ethernet.h"
+#include "ioctl.h"
+
+/** Set to 1 to enable device operations debugging.
+ */
+#define DEBUG 0
+
+/*****************************************************************************/
+
+static int eccdev_open(struct inode *, struct file *);
+static int eccdev_release(struct inode *, struct file *);
+static long eccdev_ioctl(struct file *, unsigned int, unsigned long);
+static int eccdev_mmap(struct file *, struct vm_area_struct *);
+
+/** This is the kernel version from which the .fault member of the
+ * vm_operations_struct is usable.
+ */
+#define PAGE_FAULT_VERSION KERNEL_VERSION(2, 6, 23)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 17, 0)
+# define FAULT_RETURN_TYPE int
+#else
+# define FAULT_RETURN_TYPE vm_fault_t
+#endif
+
+#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
+static FAULT_RETURN_TYPE eccdev_vma_fault(
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ struct vm_area_struct *,
+#endif
+ struct vm_fault *);
+#else
+static struct page *eccdev_vma_nopage(
+ struct vm_area_struct *, unsigned long, int *);
+#endif
+
+/*****************************************************************************/
+
+/** File operation callbacks for the EtherCAT character device.
+ */
+static struct file_operations eccdev_fops = {
+ .owner = THIS_MODULE,
+ .open = eccdev_open,
+ .release = eccdev_release,
+ .unlocked_ioctl = eccdev_ioctl,
+ .mmap = eccdev_mmap
+};
+
+/** Callbacks for a virtual memory area retrieved with ecdevc_mmap().
+ */
+struct vm_operations_struct eccdev_vm_ops = {
+#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
+ .fault = eccdev_vma_fault
+#else
+ .nopage = eccdev_vma_nopage
+#endif
+};
+
+/*****************************************************************************/
+
+/** Private data structure for file handles.
+ */
+typedef struct {
+ ec_cdev_t *cdev; /**< Character device. */
+ ec_ioctl_context_t ctx; /**< Context. */
+} ec_cdev_priv_t;
+
+/*****************************************************************************/
+
+/** Constructor.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_cdev_init(
+ ec_cdev_t *cdev, /**< EtherCAT master character device. */
+ ec_master_t *master, /**< Parent master. */
+ dev_t dev_num /**< Device number. */
+ )
+{
+ int ret;
+
+ cdev->master = master;
+
+ cdev_init(&cdev->cdev, &eccdev_fops);
+ cdev->cdev.owner = THIS_MODULE;
+
+ ret = cdev_add(&cdev->cdev,
+ MKDEV(MAJOR(dev_num), master->index), 1);
+ if (ret) {
+ EC_MASTER_ERR(master, "Failed to add character device!\n");
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_cdev_clear(ec_cdev_t *cdev /**< EtherCAT XML device */)
+{
+ cdev_del(&cdev->cdev);
+}
+
+/******************************************************************************
+ * File operations
+ *****************************************************************************/
+
+/** Called when the cdev is opened.
+ */
+int eccdev_open(struct inode *inode, struct file *filp)
+{
+ ec_cdev_t *cdev = container_of(inode->i_cdev, ec_cdev_t, cdev);
+ ec_cdev_priv_t *priv;
+
+ priv = kmalloc(sizeof(ec_cdev_priv_t), GFP_KERNEL);
+ if (!priv) {
+ EC_MASTER_ERR(cdev->master,
+ "Failed to allocate memory for private data structure.\n");
+ return -ENOMEM;
+ }
+
+ priv->cdev = cdev;
+ priv->ctx.writable = (filp->f_mode & FMODE_WRITE) != 0;
+ priv->ctx.requested = 0;
+ priv->ctx.process_data = NULL;
+ priv->ctx.process_data_size = 0;
+
+ filp->private_data = priv;
+
+#if DEBUG
+ EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
+#endif
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Called when the cdev is closed.
+ */
+int eccdev_release(struct inode *inode, struct file *filp)
+{
+ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
+ ec_master_t *master = priv->cdev->master;
+
+ if (priv->ctx.requested) {
+ ecrt_release_master(master);
+ }
+
+ if (priv->ctx.process_data) {
+ vfree(priv->ctx.process_data);
+ }
+
+#if DEBUG
+ EC_MASTER_DBG(master, 0, "File closed.\n");
+#endif
+
+ kfree(priv);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Called when an ioctl() command is issued.
+ */
+long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
+
+#if DEBUG
+ EC_MASTER_DBG(priv->cdev->master, 0,
+ "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x), arg = 0x%lx)\n",
+ filp, cmd, _IOC_NR(cmd), arg);
+#endif
+
+ return ec_ioctl(priv->cdev->master, &priv->ctx, cmd, (void __user *) arg);
+}
+
+/*****************************************************************************/
+
+#ifndef VM_DONTDUMP
+/** VM_RESERVED disappeared in 3.7.
+ */
+#define VM_DONTDUMP VM_RESERVED
+#endif
+
+/** Memory-map callback for the EtherCAT character device.
+ *
+ * The actual mapping will be done in the eccdev_vma_nopage() callback of the
+ * virtual memory area.
+ *
+ * \return Always zero (success).
+ */
+int eccdev_mmap(
+ struct file *filp,
+ struct vm_area_struct *vma
+ )
+{
+ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
+
+ EC_MASTER_DBG(priv->cdev->master, 1, "mmap()\n");
+
+ vma->vm_ops = &eccdev_vm_ops;
+ vma->vm_flags |= VM_DONTDUMP; /* Pages will not be swapped out */
+ vma->vm_private_data = priv;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+#if LINUX_VERSION_CODE >= PAGE_FAULT_VERSION
+
+/** Page fault callback for a virtual memory area.
+ *
+ * Called at the first access on a virtual-memory area retrieved with
+ * ecdev_mmap().
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static FAULT_RETURN_TYPE eccdev_vma_fault(
+#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 11, 0)
+ struct vm_area_struct *vma, /**< Virtual memory area. */
+#endif
+ struct vm_fault *vmf /**< Fault data. */
+ )
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+ struct vm_area_struct *vma = vmf->vma;
+#endif
+ unsigned long offset = vmf->pgoff << PAGE_SHIFT;
+ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
+ struct page *page;
+
+ if (offset >= priv->ctx.process_data_size) {
+ return VM_FAULT_SIGBUS;
+ }
+
+ page = vmalloc_to_page(priv->ctx.process_data + offset);
+ if (!page) {
+ return VM_FAULT_SIGBUS;
+ }
+
+ get_page(page);
+ vmf->page = page;
+
+ EC_MASTER_DBG(priv->cdev->master, 1, "Vma fault,"
+ " offset = %lu, page = %p\n", offset, page);
+
+ return 0;
+}
+
+#else
+
+/** Nopage callback for a virtual memory area.
+ *
+ * Called at the first access on a virtual-memory area retrieved with
+ * ecdev_mmap().
+ */
+struct page *eccdev_vma_nopage(
+ struct vm_area_struct *vma, /**< Virtual memory area initialized by
+ the kernel. */
+ unsigned long address, /**< Requested virtual address. */
+ int *type /**< Type output parameter. */
+ )
+{
+ unsigned long offset;
+ struct page *page = NOPAGE_SIGBUS;
+ ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
+ ec_master_t *master = priv->cdev->master;
+
+ offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
+
+ if (offset >= priv->ctx.process_data_size)
+ return NOPAGE_SIGBUS;
+
+ page = vmalloc_to_page(priv->ctx.process_data + offset);
+
+ EC_MASTER_DBG(master, 1, "Nopage fault vma, address = %#lx,"
+ " offset = %#lx, page = %p\n", address, offset, page);
+
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+
+ return page;
+}
+
+#endif
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/cdev.h b/net/ethercat/master/cdev.h
new file mode 100644
index 000000000000..a06bb1c90562
--- /dev/null
+++ b/net/ethercat/master/cdev.h
@@ -0,0 +1,61 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 master character device.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_CDEV_H__
+#define __EC_CDEV_H__
+
+#include <linux/fs.h>
+#include <linux/cdev.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** EtherCAT master character device.
+*/
+typedef struct {
+ ec_master_t *master; /**< Master owning the device. */
+ struct cdev cdev; /**< Character device. */
+} ec_cdev_t;
+
+/*****************************************************************************/
+
+int ec_cdev_init(ec_cdev_t *, ec_master_t *, dev_t);
+void ec_cdev_clear(ec_cdev_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/coe_emerg_ring.c b/net/ethercat/master/coe_emerg_ring.c
new file mode 100644
index 000000000000..82c33db9aeab
--- /dev/null
+++ b/net/ethercat/master/coe_emerg_ring.c
@@ -0,0 +1,177 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2012 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
+ *
+ *****************************************************************************/
+
+/** \file
+ * EtherCAT CoE emergency ring buffer methods.
+ */
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "coe_emerg_ring.h"
+
+/*****************************************************************************/
+
+/** Emergency ring buffer constructor.
+ */
+void ec_coe_emerg_ring_init(
+ ec_coe_emerg_ring_t *ring, /**< Emergency ring. */
+ ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ ring->sc = sc;
+ ring->msgs = NULL;
+ ring->size = 0;
+ ring->read_index = 0;
+ ring->write_index = 0;
+ ring->overruns = 0;
+}
+
+/*****************************************************************************/
+
+/** Emergency ring buffer destructor.
+ */
+void ec_coe_emerg_ring_clear(
+ ec_coe_emerg_ring_t *ring /**< Emergency ring. */
+ )
+{
+ if (ring->msgs) {
+ kfree(ring->msgs);
+ }
+}
+
+/*****************************************************************************/
+
+/** Set the ring size.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_coe_emerg_ring_size(
+ ec_coe_emerg_ring_t *ring, /**< Emergency ring. */
+ size_t size /**< Maximum number of messages in the ring. */
+ )
+{
+ ring->size = 0;
+
+ if (size < 0) {
+ size = 0;
+ }
+
+ ring->read_index = ring->write_index = 0;
+
+ if (ring->msgs) {
+ kfree(ring->msgs);
+ }
+ ring->msgs = NULL;
+
+ if (size == 0) {
+ return 0;
+ }
+
+ ring->msgs = kmalloc(sizeof(ec_coe_emerg_msg_t) * (size + 1), GFP_KERNEL);
+ if (!ring->msgs) {
+ return -ENOMEM;
+ }
+
+ ring->size = size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Add a new emergency message.
+ */
+void ec_coe_emerg_ring_push(
+ ec_coe_emerg_ring_t *ring, /**< Emergency ring. */
+ const u8 *msg /**< Emergency message. */
+ )
+{
+ if (!ring->size ||
+ (ring->write_index + 1) % (ring->size + 1) == ring->read_index) {
+ ring->overruns++;
+ return;
+ }
+
+ memcpy(ring->msgs[ring->write_index].data, msg,
+ EC_COE_EMERGENCY_MSG_SIZE);
+ ring->write_index = (ring->write_index + 1) % (ring->size + 1);
+}
+
+/*****************************************************************************/
+
+/** Remove an emergency message from the ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_coe_emerg_ring_pop(
+ ec_coe_emerg_ring_t *ring, /**< Emergency ring. */
+ u8 *msg /**< Memory to store the emergency message. */
+ )
+{
+ if (ring->read_index == ring->write_index) {
+ return -ENOENT;
+ }
+
+ memcpy(msg, ring->msgs[ring->read_index].data, EC_COE_EMERGENCY_MSG_SIZE);
+ ring->read_index = (ring->read_index + 1) % (ring->size + 1);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Clear the ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_coe_emerg_ring_clear_ring(
+ ec_coe_emerg_ring_t *ring /**< Emergency ring. */
+ )
+{
+ ring->read_index = ring->write_index;
+ ring->overruns = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read the number of overruns.
+ *
+ * \return Number of overruns.
+ */
+int ec_coe_emerg_ring_overruns(
+ ec_coe_emerg_ring_t *ring /**< Emergency ring. */
+ )
+{
+ return ring->overruns;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/coe_emerg_ring.h b/net/ethercat/master/coe_emerg_ring.h
new file mode 100644
index 000000000000..c2f6438ca495
--- /dev/null
+++ b/net/ethercat/master/coe_emerg_ring.h
@@ -0,0 +1,78 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2012 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 CoE emergency ring buffer structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_COE_EMERG_RING_H__
+#define __EC_COE_EMERG_RING_H__
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** EtherCAT CoE emergency message record.
+ */
+typedef struct {
+ u8 data[EC_COE_EMERGENCY_MSG_SIZE]; /**< Message data. */
+} ec_coe_emerg_msg_t;
+
+/*****************************************************************************/
+
+/** EtherCAT CoE emergency ring buffer.
+ */
+typedef struct {
+ ec_slave_config_t *sc; /**< Slave configuration owning the ring. */
+
+ ec_coe_emerg_msg_t *msgs; /**< Message ring. */
+ size_t size; /**< Ring size. */
+
+ unsigned int read_index; /**< Read index. */
+ unsigned int write_index; /**< Write index. */
+ unsigned int overruns; /**< Number of overruns since last reset. */
+} ec_coe_emerg_ring_t;
+
+/*****************************************************************************/
+
+void ec_coe_emerg_ring_init(ec_coe_emerg_ring_t *, ec_slave_config_t *);
+void ec_coe_emerg_ring_clear(ec_coe_emerg_ring_t *);
+
+int ec_coe_emerg_ring_size(ec_coe_emerg_ring_t *, size_t);
+void ec_coe_emerg_ring_push(ec_coe_emerg_ring_t *, const u8 *);
+int ec_coe_emerg_ring_pop(ec_coe_emerg_ring_t *, u8 *);
+int ec_coe_emerg_ring_clear_ring(ec_coe_emerg_ring_t *);
+int ec_coe_emerg_ring_overruns(ec_coe_emerg_ring_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/datagram.c b/net/ethercat/master/datagram.c
new file mode 100644
index 000000000000..f6163274dd7d
--- /dev/null
+++ b/net/ethercat/master/datagram.c
@@ -0,0 +1,649 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Methods of an EtherCAT datagram.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "datagram.h"
+#include "master.h"
+
+/*****************************************************************************/
+
+/** \cond */
+
+#define EC_FUNC_HEADER \
+ ret = ec_datagram_prealloc(datagram, data_size); \
+ if (unlikely(ret)) \
+ return ret; \
+ datagram->index = 0; \
+ datagram->working_counter = 0; \
+ datagram->state = EC_DATAGRAM_INIT;
+
+#define EC_FUNC_FOOTER \
+ datagram->data_size = data_size; \
+ return 0;
+
+/** \endcond */
+
+/*****************************************************************************/
+
+/** Array of datagram type strings used in ec_datagram_type_string().
+ *
+ * \attention This is indexed by ec_datagram_type_t.
+ */
+static const char *type_strings[] = {
+ "?",
+ "APRD",
+ "APWR",
+ "APRW",
+ "FPRD",
+ "FPWR",
+ "FPRW",
+ "BRD",
+ "BWR",
+ "BRW",
+ "LRD",
+ "LWR",
+ "LRW",
+ "ARMW",
+ "FRMW"
+};
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_datagram_init(ec_datagram_t *datagram /**< EtherCAT datagram. */)
+{
+ INIT_LIST_HEAD(&datagram->queue); // mark as unqueued
+ datagram->device_index = EC_DEVICE_MAIN;
+ datagram->type = EC_DATAGRAM_NONE;
+ memset(datagram->address, 0x00, EC_ADDR_LEN);
+ datagram->data = NULL;
+ datagram->data_origin = EC_ORIG_INTERNAL;
+ datagram->mem_size = 0;
+ datagram->data_size = 0;
+ datagram->index = 0x00;
+ datagram->working_counter = 0x0000;
+ datagram->state = EC_DATAGRAM_INIT;
+#ifdef EC_HAVE_CYCLES
+ datagram->cycles_sent = 0;
+#endif
+ datagram->jiffies_sent = 0;
+#ifdef EC_HAVE_CYCLES
+ datagram->cycles_received = 0;
+#endif
+ datagram->jiffies_received = 0;
+ datagram->skip_count = 0;
+ datagram->stats_output_jiffies = 0;
+ memset(datagram->name, 0x00, EC_DATAGRAM_NAME_SIZE);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_datagram_clear(ec_datagram_t *datagram /**< EtherCAT datagram. */)
+{
+ ec_datagram_unqueue(datagram);
+
+ if (datagram->data_origin == EC_ORIG_INTERNAL && datagram->data) {
+ kfree(datagram->data);
+ datagram->data = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/** Unqueue datagram.
+ */
+void ec_datagram_unqueue(ec_datagram_t *datagram /**< EtherCAT datagram. */)
+{
+ if (!list_empty(&datagram->queue)) {
+ list_del_init(&datagram->queue);
+ }
+}
+
+/*****************************************************************************/
+
+/** Allocates internal payload memory.
+ *
+ * If the allocated memory is already larger than requested, nothing ist done.
+ *
+ * \attention If external payload memory has been provided, no range checking
+ * is done!
+ *
+ * \return 0 in case of success, otherwise \a -ENOMEM.
+ */
+int ec_datagram_prealloc(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ size_t size /**< New payload size in bytes. */
+ )
+{
+ if (datagram->data_origin == EC_ORIG_EXTERNAL
+ || size <= datagram->mem_size)
+ return 0;
+
+ if (datagram->data) {
+ kfree(datagram->data);
+ datagram->data = NULL;
+ datagram->mem_size = 0;
+ }
+
+ if (!(datagram->data = kmalloc(size, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate %zu bytes of datagram memory!\n", size);
+ return -ENOMEM;
+ }
+
+ datagram->mem_size = size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Fills the datagram payload memory with zeros.
+ */
+void ec_datagram_zero(ec_datagram_t *datagram /**< EtherCAT datagram. */)
+{
+ memset(datagram->data, 0x00, datagram->data_size);
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT APRD datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_aprd(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t ring_position, /**< Auto-increment address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to read. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_APRD;
+ EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1));
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT APWR datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_apwr(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t ring_position, /**< Auto-increment address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_APWR;
+ EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1));
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT APRW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_aprw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t ring_position, /**< Auto-increment address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_APRW;
+ EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1));
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT ARMW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_armw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t ring_position, /**< Auto-increment address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to read. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_ARMW;
+ EC_WRITE_S16(datagram->address, (int16_t) ring_position * (-1));
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT FPRD datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_fprd(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t configured_address, /**< Configured station address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to read. */
+ )
+{
+ int ret;
+
+ if (unlikely(configured_address == 0x0000))
+ EC_WARN("Using configured station address 0x0000!\n");
+
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_FPRD;
+ EC_WRITE_U16(datagram->address, configured_address);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT FPWR datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_fpwr(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t configured_address, /**< Configured station address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+
+ if (unlikely(configured_address == 0x0000))
+ EC_WARN("Using configured station address 0x0000!\n");
+
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_FPWR;
+ EC_WRITE_U16(datagram->address, configured_address);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT FPRW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_fprw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t configured_address, /**< Configured station address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+
+ if (unlikely(configured_address == 0x0000))
+ EC_WARN("Using configured station address 0x0000!\n");
+
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_FPRW;
+ EC_WRITE_U16(datagram->address, configured_address);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT FRMW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_frmw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t configured_address, /**< Configured station address. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+
+ if (unlikely(configured_address == 0x0000))
+ EC_WARN("Using configured station address 0x0000!\n");
+
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_FRMW;
+ EC_WRITE_U16(datagram->address, configured_address);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT BRD datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_brd(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to read. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_BRD;
+ EC_WRITE_U16(datagram->address, 0x0000);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT BWR datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_bwr(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_BWR;
+ EC_WRITE_U16(datagram->address, 0x0000);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT BRW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_brw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint16_t mem_address, /**< Physical memory address. */
+ size_t data_size /**< Number of bytes to write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_BRW;
+ EC_WRITE_U16(datagram->address, 0x0000);
+ EC_WRITE_U16(datagram->address + 2, mem_address);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRD datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrd(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRD;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LWR datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lwr(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LWR;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRW datagram.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrw(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size /**< Number of bytes to read/write. */
+ )
+{
+ int ret;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRW;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRD datagram with external memory.
+ *
+ * \attention It is assumed, that the external memory is at least \a data_size
+ * bytes large.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrd_ext(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size, /**< Number of bytes to read/write. */
+ uint8_t *external_memory /**< Pointer to the memory to use. */
+ )
+{
+ int ret;
+ datagram->data = external_memory;
+ datagram->data_origin = EC_ORIG_EXTERNAL;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRD;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LWR datagram with external memory.
+ *
+ * \attention It is assumed, that the external memory is at least \a data_size
+ * bytes large.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lwr_ext(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size, /**< Number of bytes to read/write. */
+ uint8_t *external_memory /**< Pointer to the memory to use. */
+ )
+{
+ int ret;
+ datagram->data = external_memory;
+ datagram->data_origin = EC_ORIG_EXTERNAL;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LWR;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Initializes an EtherCAT LRW datagram with external memory.
+ *
+ * \attention It is assumed, that the external memory is at least \a data_size
+ * bytes large.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_datagram_lrw_ext(
+ ec_datagram_t *datagram, /**< EtherCAT datagram. */
+ uint32_t offset, /**< Logical address. */
+ size_t data_size, /**< Number of bytes to read/write. */
+ uint8_t *external_memory /**< Pointer to the memory to use. */
+ )
+{
+ int ret;
+ datagram->data = external_memory;
+ datagram->data_origin = EC_ORIG_EXTERNAL;
+ EC_FUNC_HEADER;
+ datagram->type = EC_DATAGRAM_LRW;
+ EC_WRITE_U32(datagram->address, offset);
+ EC_FUNC_FOOTER;
+}
+
+/*****************************************************************************/
+
+/** Prints the state of a datagram.
+ *
+ * Outputs a text message.
+ */
+void ec_datagram_print_state(
+ const ec_datagram_t *datagram /**< EtherCAT datagram */
+ )
+{
+ printk("Datagram ");
+ switch (datagram->state) {
+ case EC_DATAGRAM_INIT:
+ printk("initialized");
+ break;
+ case EC_DATAGRAM_QUEUED:
+ printk("queued");
+ break;
+ case EC_DATAGRAM_SENT:
+ printk("sent");
+ break;
+ case EC_DATAGRAM_RECEIVED:
+ printk("received");
+ break;
+ case EC_DATAGRAM_TIMED_OUT:
+ printk("timed out");
+ break;
+ case EC_DATAGRAM_ERROR:
+ printk("error");
+ break;
+ default:
+ printk("???");
+ }
+
+ printk(".\n");
+}
+
+/*****************************************************************************/
+
+/** Evaluates the working counter of a single-cast datagram.
+ *
+ * Outputs an error message.
+ */
+void ec_datagram_print_wc_error(
+ const ec_datagram_t *datagram /**< EtherCAT datagram */
+ )
+{
+ if (datagram->working_counter == 0)
+ printk("No response.");
+ else if (datagram->working_counter > 1)
+ printk("%u slaves responded!", datagram->working_counter);
+ else
+ printk("Success.");
+ printk("\n");
+}
+
+/*****************************************************************************/
+
+/** Outputs datagram statistics at most every second.
+ */
+void ec_datagram_output_stats(
+ ec_datagram_t *datagram
+ )
+{
+ if (jiffies - datagram->stats_output_jiffies > HZ) {
+ datagram->stats_output_jiffies = jiffies;
+
+ if (unlikely(datagram->skip_count)) {
+ EC_WARN("Datagram %p (%s) was SKIPPED %u time%s.\n",
+ datagram, datagram->name,
+ datagram->skip_count,
+ datagram->skip_count == 1 ? "" : "s");
+ datagram->skip_count = 0;
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** Returns a string describing the datagram type.
+ *
+ * \return Pointer on a static memory containing the requested string.
+ */
+const char *ec_datagram_type_string(
+ const ec_datagram_t *datagram /**< EtherCAT datagram. */
+ )
+{
+ return type_strings[datagram->type];
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/datagram.h b/net/ethercat/master/datagram.h
new file mode 100644
index 000000000000..e902fbd43789
--- /dev/null
+++ b/net/ethercat/master/datagram.h
@@ -0,0 +1,148 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 datagram structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DATAGRAM_H__
+#define __EC_DATAGRAM_H__
+
+#include <linux/list.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** EtherCAT datagram type.
+ */
+typedef enum {
+ EC_DATAGRAM_NONE = 0x00, /**< Dummy. */
+ EC_DATAGRAM_APRD = 0x01, /**< Auto Increment Physical Read. */
+ EC_DATAGRAM_APWR = 0x02, /**< Auto Increment Physical Write. */
+ EC_DATAGRAM_APRW = 0x03, /**< Auto Increment Physical ReadWrite. */
+ EC_DATAGRAM_FPRD = 0x04, /**< Configured Address Physical Read. */
+ EC_DATAGRAM_FPWR = 0x05, /**< Configured Address Physical Write. */
+ EC_DATAGRAM_FPRW = 0x06, /**< Configured Address Physical ReadWrite. */
+ EC_DATAGRAM_BRD = 0x07, /**< Broadcast Read. */
+ EC_DATAGRAM_BWR = 0x08, /**< Broadcast Write. */
+ EC_DATAGRAM_BRW = 0x09, /**< Broadcast ReadWrite. */
+ EC_DATAGRAM_LRD = 0x0A, /**< Logical Read. */
+ EC_DATAGRAM_LWR = 0x0B, /**< Logical Write. */
+ EC_DATAGRAM_LRW = 0x0C, /**< Logical ReadWrite. */
+ EC_DATAGRAM_ARMW = 0x0D, /**< Auto Increment Physical Read Multiple
+ Write. */
+ EC_DATAGRAM_FRMW = 0x0E, /**< Configured Address Physical Read Multiple
+ Write. */
+} ec_datagram_type_t;
+
+/*****************************************************************************/
+
+/** EtherCAT datagram state.
+ */
+typedef enum {
+ EC_DATAGRAM_INIT, /**< Initial state of a new datagram. */
+ EC_DATAGRAM_QUEUED, /**< Queued for sending. */
+ EC_DATAGRAM_SENT, /**< Sent (still in the queue). */
+ EC_DATAGRAM_RECEIVED, /**< Received (dequeued). */
+ EC_DATAGRAM_TIMED_OUT, /**< Timed out (dequeued). */
+ EC_DATAGRAM_ERROR /**< Error while sending/receiving (dequeued). */
+} ec_datagram_state_t;
+
+/*****************************************************************************/
+
+/** EtherCAT datagram.
+ */
+typedef struct {
+ struct list_head queue; /**< Master datagram queue item. */
+ struct list_head sent; /**< Master list item for sent datagrams. */
+ ec_device_index_t device_index; /**< Device via which the datagram shall
+ be / was sent. */
+ ec_datagram_type_t type; /**< Datagram type (APRD, BWR, etc.). */
+ uint8_t address[EC_ADDR_LEN]; /**< Recipient address. */
+ uint8_t *data; /**< Datagram payload. */
+ ec_origin_t data_origin; /**< Origin of the \a data memory. */
+ size_t mem_size; /**< Datagram \a data memory size. */
+ size_t data_size; /**< Size of the data in \a data. */
+ uint8_t index; /**< Index (set by master). */
+ uint16_t working_counter; /**< Working counter. */
+ ec_datagram_state_t state; /**< State. */
+#ifdef EC_HAVE_CYCLES
+ cycles_t cycles_sent; /**< Time, when the datagram was sent. */
+#endif
+ unsigned long jiffies_sent; /**< Jiffies, when the datagram was sent. */
+#ifdef EC_HAVE_CYCLES
+ cycles_t cycles_received; /**< Time, when the datagram was received. */
+#endif
+ unsigned long jiffies_received; /**< Jiffies, when the datagram was
+ received. */
+ unsigned int skip_count; /**< Number of requeues when not yet received. */
+ unsigned long stats_output_jiffies; /**< Last statistics output. */
+ char name[EC_DATAGRAM_NAME_SIZE]; /**< Description of the datagram. */
+} ec_datagram_t;
+
+/*****************************************************************************/
+
+void ec_datagram_init(ec_datagram_t *);
+void ec_datagram_clear(ec_datagram_t *);
+void ec_datagram_unqueue(ec_datagram_t *);
+int ec_datagram_prealloc(ec_datagram_t *, size_t);
+void ec_datagram_zero(ec_datagram_t *);
+
+int ec_datagram_aprd(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_apwr(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_aprw(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_armw(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_fprd(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_fpwr(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_fprw(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_frmw(ec_datagram_t *, uint16_t, uint16_t, size_t);
+int ec_datagram_brd(ec_datagram_t *, uint16_t, size_t);
+int ec_datagram_bwr(ec_datagram_t *, uint16_t, size_t);
+int ec_datagram_brw(ec_datagram_t *, uint16_t, size_t);
+int ec_datagram_lrd(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lwr(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lrw(ec_datagram_t *, uint32_t, size_t);
+int ec_datagram_lrd_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+int ec_datagram_lwr_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+int ec_datagram_lrw_ext(ec_datagram_t *, uint32_t, size_t, uint8_t *);
+
+void ec_datagram_print_state(const ec_datagram_t *);
+void ec_datagram_print_wc_error(const ec_datagram_t *);
+void ec_datagram_output_stats(ec_datagram_t *);
+const char *ec_datagram_type_string(const ec_datagram_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/datagram_pair.c b/net/ethercat/master/datagram_pair.c
new file mode 100644
index 000000000000..656c9e9ebdf4
--- /dev/null
+++ b/net/ethercat/master/datagram_pair.c
@@ -0,0 +1,201 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 datagram pair methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "master.h"
+#include "datagram_pair.h"
+
+/*****************************************************************************/
+
+/** Datagram pair constructor.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_datagram_pair_init(
+ ec_datagram_pair_t *pair, /**< Datagram pair. */
+ ec_domain_t *domain, /**< Parent domain. */
+ uint32_t logical_offset, /**< Logical offset. */
+ uint8_t *data, /**< Data pointer. */
+ size_t data_size, /**< Data size. */
+ const unsigned int used[] /**< input/output use count. */
+ )
+{
+ ec_device_index_t dev_idx;
+ int ret;
+
+ INIT_LIST_HEAD(&pair->list);
+ pair->domain = domain;
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_init(&pair->datagrams[dev_idx]);
+ snprintf(pair->datagrams[dev_idx].name,
+ EC_DATAGRAM_NAME_SIZE, "domain%u-%u-%s", domain->index,
+ logical_offset, ec_device_names[dev_idx != 0]);
+ pair->datagrams[dev_idx].device_index = dev_idx;
+ }
+
+ pair->expected_working_counter = 0U;
+
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ /* backup datagrams have their own memory */
+ ret = ec_datagram_prealloc(&pair->datagrams[dev_idx], data_size);
+ if (ret) {
+ goto out_datagrams;
+ }
+ }
+
+#if EC_MAX_NUM_DEVICES > 1
+ if (!(pair->send_buffer = kmalloc(data_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(domain->master,
+ "Failed to allocate domain send buffer!\n");
+ ret = -ENOMEM;
+ goto out_datagrams;
+ }
+#endif
+
+ /* The ec_datagram_lxx() calls below can not fail, because either the
+ * datagram has external memory or it is preallocated. */
+
+ if (used[EC_DIR_OUTPUT] && used[EC_DIR_INPUT]) { // inputs and outputs
+ ec_datagram_lrw_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_lrw(&pair->datagrams[dev_idx],
+ logical_offset, data_size);
+ }
+
+ // If LRW is used, output FMMUs increment the working counter by 2,
+ // while input FMMUs increment it by 1.
+ pair->expected_working_counter =
+ used[EC_DIR_OUTPUT] * 2 + used[EC_DIR_INPUT];
+ } else if (used[EC_DIR_OUTPUT]) { // outputs only
+ ec_datagram_lwr_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_lwr(&pair->datagrams[dev_idx],
+ logical_offset, data_size);
+ }
+
+ pair->expected_working_counter = used[EC_DIR_OUTPUT];
+ } else { // inputs only (or nothing)
+ ec_datagram_lrd_ext(&pair->datagrams[EC_DEVICE_MAIN],
+ logical_offset, data_size, data);
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_lrd(&pair->datagrams[dev_idx], logical_offset,
+ data_size);
+ }
+
+ pair->expected_working_counter = used[EC_DIR_INPUT];
+ }
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_zero(&pair->datagrams[dev_idx]);
+ }
+
+ return 0;
+
+out_datagrams:
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ ec_datagram_clear(&pair->datagrams[dev_idx]);
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Datagram pair destructor.
+ */
+void ec_datagram_pair_clear(
+ ec_datagram_pair_t *pair /**< Datagram pair. */
+ )
+{
+ unsigned int dev_idx;
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(pair->domain->master);
+ dev_idx++) {
+ ec_datagram_clear(&pair->datagrams[dev_idx]);
+ }
+
+#if EC_MAX_NUM_DEVICES > 1
+ if (pair->send_buffer) {
+ kfree(pair->send_buffer);
+ }
+#endif
+}
+
+/*****************************************************************************/
+
+/** Process received data.
+ *
+ * \return Working counter sum over all devices.
+ */
+uint16_t ec_datagram_pair_process(
+ ec_datagram_pair_t *pair, /**< Datagram pair. */
+ uint16_t wc_sum[] /**< Working counter sums. */
+ )
+{
+ unsigned int dev_idx;
+ uint16_t pair_wc = 0;
+
+ for (dev_idx = 0; dev_idx < ec_master_num_devices(pair->domain->master);
+ dev_idx++) {
+ ec_datagram_t *datagram = &pair->datagrams[dev_idx];
+
+#ifdef EC_RT_SYSLOG
+ ec_datagram_output_stats(datagram);
+#endif
+
+ if (datagram->state == EC_DATAGRAM_RECEIVED) {
+ pair_wc += datagram->working_counter;
+ wc_sum[dev_idx] += datagram->working_counter;
+ }
+ }
+
+ return pair_wc;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/datagram_pair.h b/net/ethercat/master/datagram_pair.h
new file mode 100644
index 000000000000..c6da2b318529
--- /dev/null
+++ b/net/ethercat/master/datagram_pair.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 datagram pair structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DATAGRAM_PAIR_H__
+#define __EC_DATAGRAM_PAIR_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "datagram.h"
+
+/*****************************************************************************/
+
+/** Domain datagram pair.
+ */
+typedef struct {
+ struct list_head list; /**< List header. */
+ ec_domain_t *domain; /**< Parent domain. */
+ ec_datagram_t datagrams[EC_MAX_NUM_DEVICES]; /**< Datagrams. */
+#if EC_MAX_NUM_DEVICES > 1
+ uint8_t *send_buffer;
+#endif
+ unsigned int expected_working_counter; /**< Expectord working conter. */
+} ec_datagram_pair_t;
+
+/*****************************************************************************/
+
+int ec_datagram_pair_init(ec_datagram_pair_t *, ec_domain_t *, uint32_t,
+ uint8_t *, size_t, const unsigned int []);
+void ec_datagram_pair_clear(ec_datagram_pair_t *);
+
+uint16_t ec_datagram_pair_process(ec_datagram_pair_t *, uint16_t[]);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/debug.c b/net/ethercat/master/debug.c
new file mode 100644
index 000000000000..4246f2d07869
--- /dev/null
+++ b/net/ethercat/master/debug.c
@@ -0,0 +1,266 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 interface for debugging purposes.
+*/
+
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "globals.h"
+#include "master.h"
+#include "debug.h"
+
+/*****************************************************************************/
+
+// net_device functions
+int ec_dbgdev_open(struct net_device *);
+int ec_dbgdev_stop(struct net_device *);
+int ec_dbgdev_tx(struct sk_buff *, struct net_device *);
+struct net_device_stats *ec_dbgdev_stats(struct net_device *);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+/** Device operations for debug interfaces.
+ */
+static const struct net_device_ops ec_dbg_netdev_ops =
+{
+ .ndo_open = ec_dbgdev_open,
+ .ndo_stop = ec_dbgdev_stop,
+ .ndo_start_xmit = ec_dbgdev_tx,
+ .ndo_get_stats = ec_dbgdev_stats,
+};
+#endif
+
+/*****************************************************************************/
+
+/** Debug interface constructor.
+ *
+ * Initializes the debug object, creates a net_device and registeres it.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_debug_init(
+ ec_debug_t *dbg, /**< Debug object. */
+ ec_device_t *device, /**< EtherCAT device. */
+ const char *name /**< Interface name. */
+ )
+{
+ dbg->device = device;
+ dbg->registered = 0;
+ dbg->opened = 0;
+
+ memset(&dbg->stats, 0, sizeof(struct net_device_stats));
+
+ if (!(dbg->dev =
+ alloc_netdev(sizeof(ec_debug_t *), name, NET_NAME_UNKNOWN, ether_setup))) {
+ EC_MASTER_ERR(device->master, "Unable to allocate net_device"
+ " for debug object!\n");
+ return -ENODEV;
+ }
+
+ // initialize net_device
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 31)
+ dbg->dev->netdev_ops = &ec_dbg_netdev_ops;
+#else
+ dbg->dev->open = ec_dbgdev_open;
+ dbg->dev->stop = ec_dbgdev_stop;
+ dbg->dev->hard_start_xmit = ec_dbgdev_tx;
+ dbg->dev->get_stats = ec_dbgdev_stats;
+#endif
+
+ // initialize private data
+ *((ec_debug_t **) netdev_priv(dbg->dev)) = dbg;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Debug interface destructor.
+ *
+ * Unregisters the net_device and frees allocated memory.
+ */
+void ec_debug_clear(
+ ec_debug_t *dbg /**< debug object */
+ )
+{
+ ec_debug_unregister(dbg);
+ free_netdev(dbg->dev);
+}
+
+/*****************************************************************************/
+
+/** Register debug interface.
+ */
+void ec_debug_register(
+ ec_debug_t *dbg, /**< debug object */
+ const struct net_device *net_dev /**< 'Real' Ethernet device. */
+ )
+{
+ int result;
+
+ ec_debug_unregister(dbg);
+
+ // use the Ethernet address of the physical device for the debug device
+ memcpy(dbg->dev->dev_addr, net_dev->dev_addr, ETH_ALEN);
+
+ // connect the net_device to the kernel
+ if ((result = register_netdev(dbg->dev))) {
+ EC_MASTER_WARN(dbg->device->master, "Unable to register net_device:"
+ " error %i\n", result);
+ } else {
+ dbg->registered = 1;
+ }
+}
+
+/*****************************************************************************/
+
+/** Unregister debug interface.
+ */
+void ec_debug_unregister(
+ ec_debug_t *dbg /**< debug object */
+ )
+{
+ if (dbg->registered) {
+ dbg->opened = 0;
+ dbg->registered = 0;
+ unregister_netdev(dbg->dev);
+ }
+}
+
+/*****************************************************************************/
+
+/** Sends frame data to the interface.
+ */
+void ec_debug_send(
+ ec_debug_t *dbg, /**< debug object */
+ const uint8_t *data, /**< frame data */
+ size_t size /**< size of the frame data */
+ )
+{
+ struct sk_buff *skb;
+
+ if (!dbg->opened)
+ return;
+
+ // allocate socket buffer
+ if (!(skb = dev_alloc_skb(size))) {
+ dbg->stats.rx_dropped++;
+ return;
+ }
+
+ // copy frame contents into socket buffer
+ memcpy(skb_put(skb, size), data, size);
+
+ // update device statistics
+ dbg->stats.rx_packets++;
+ dbg->stats.rx_bytes += size;
+
+ // pass socket buffer to network stack
+ skb->dev = dbg->dev;
+ skb->protocol = eth_type_trans(skb, dbg->dev);
+ skb->ip_summed = CHECKSUM_UNNECESSARY;
+ netif_rx(skb);
+}
+
+/******************************************************************************
+ * NET_DEVICE functions
+ *****************************************************************************/
+
+/** Opens the virtual network device.
+ *
+ * \return Always zero (success).
+ */
+int ec_dbgdev_open(
+ struct net_device *dev /**< debug net_device */
+ )
+{
+ ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
+ dbg->opened = 1;
+ EC_MASTER_INFO(dbg->device->master, "Debug interface %s opened.\n",
+ dev->name);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Stops the virtual network device.
+ *
+ * \return Always zero (success).
+ */
+int ec_dbgdev_stop(
+ struct net_device *dev /**< debug net_device */
+ )
+{
+ ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
+ dbg->opened = 0;
+ EC_MASTER_INFO(dbg->device->master, "Debug interface %s stopped.\n",
+ dev->name);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Transmits data via the virtual network device.
+ *
+ * \return Always zero (success).
+ */
+int ec_dbgdev_tx(
+ struct sk_buff *skb, /**< transmit socket buffer */
+ struct net_device *dev /**< EoE net_device */
+ )
+{
+ ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
+
+ dev_kfree_skb(skb);
+ dbg->stats.tx_dropped++;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets statistics about the virtual network device.
+ *
+ * \return Statistics.
+ */
+struct net_device_stats *ec_dbgdev_stats(
+ struct net_device *dev /**< debug net_device */
+ )
+{
+ ec_debug_t *dbg = *((ec_debug_t **) netdev_priv(dev));
+ return &dbg->stats;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/debug.h b/net/ethercat/master/debug.h
new file mode 100644
index 000000000000..3439f9f03feb
--- /dev/null
+++ b/net/ethercat/master/debug.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Network interface for debugging purposes.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DEBUG_H__
+#define __EC_DEBUG_H__
+
+#include <net/ecdev.h>
+
+/*****************************************************************************/
+
+/** Debugging network interface.
+ */
+typedef struct
+{
+ ec_device_t *device; /**< Parent device. */
+ struct net_device *dev; /**< net_device for virtual ethernet device */
+ struct net_device_stats stats; /**< device statistics */
+ uint8_t registered; /**< net_device is opened */
+ uint8_t opened; /**< net_device is opened */
+}
+ec_debug_t;
+
+/*****************************************************************************/
+
+int ec_debug_init(ec_debug_t *, ec_device_t *, const char *);
+void ec_debug_clear(ec_debug_t *);
+void ec_debug_register(ec_debug_t *, const struct net_device *);
+void ec_debug_unregister(ec_debug_t *);
+void ec_debug_send(ec_debug_t *, const uint8_t *, size_t);
+
+#endif
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/device.c b/net/ethercat/master/device.c
new file mode 100644
index 000000000000..7edb359dcada
--- /dev/null
+++ b/net/ethercat/master/device.c
@@ -0,0 +1,737 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 device methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/skbuff.h>
+#include <linux/if_ether.h>
+#include <linux/netdevice.h>
+#include <linux/rtnetlink.h>
+
+#include "device.h"
+#include "master.h"
+
+#ifdef EC_DEBUG_RING
+#define timersub(a, b, result) \
+ do { \
+ (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \
+ (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \
+ if ((result)->tv_usec < 0) { \
+ --(result)->tv_sec; \
+ (result)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+
+/*****************************************************************************/
+
+/** Constructor.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_device_init(
+ ec_device_t *device, /**< EtherCAT device */
+ ec_master_t *master /**< master owning the device */
+ )
+{
+ int ret;
+ unsigned int i;
+ struct ethhdr *eth;
+#ifdef EC_DEBUG_IF
+ char ifname[10];
+ char mb = 'x';
+#endif
+
+ device->master = master;
+ device->dev = NULL;
+ device->poll = NULL;
+ device->module = NULL;
+ device->open = 0;
+ device->link_state = 0;
+ for (i = 0; i < EC_TX_RING_SIZE; i++) {
+ device->tx_skb[i] = NULL;
+ }
+ device->tx_ring_index = 0;
+#ifdef EC_HAVE_CYCLES
+ device->cycles_poll = 0;
+#endif
+#ifdef EC_DEBUG_RING
+ device->timeval_poll.tv_sec = 0;
+ device->timeval_poll.tv_usec = 0;
+#endif
+ device->jiffies_poll = 0;
+
+ ec_device_clear_stats(device);
+
+#ifdef EC_DEBUG_RING
+ for (i = 0; i < EC_DEBUG_RING_SIZE; i++) {
+ ec_debug_frame_t *df = &device->debug_frames[i];
+ df->dir = TX;
+ df->t.tv_sec = 0;
+ df->t.tv_usec = 0;
+ memset(df->data, 0, EC_MAX_DATA_SIZE);
+ df->data_size = 0;
+ }
+#endif
+#ifdef EC_DEBUG_RING
+ device->debug_frame_index = 0;
+ device->debug_frame_count = 0;
+#endif
+
+#ifdef EC_DEBUG_IF
+ if (device == &master->devices[EC_DEVICE_MAIN]) {
+ mb = 'm';
+ }
+ else {
+ mb = 'b';
+ }
+
+ sprintf(ifname, "ecdbg%c%u", mb, master->index);
+
+ ret = ec_debug_init(&device->dbg, device, ifname);
+ if (ret < 0) {
+ EC_MASTER_ERR(master, "Failed to init debug device!\n");
+ goto out_return;
+ }
+#endif
+
+ for (i = 0; i < EC_TX_RING_SIZE; i++) {
+ if (!(device->tx_skb[i] = dev_alloc_skb(ETH_FRAME_LEN))) {
+ EC_MASTER_ERR(master, "Error allocating device socket buffer!\n");
+ ret = -ENOMEM;
+ goto out_tx_ring;
+ }
+
+ // add Ethernet-II-header
+ skb_reserve(device->tx_skb[i], ETH_HLEN);
+ eth = (struct ethhdr *) skb_push(device->tx_skb[i], ETH_HLEN);
+ eth->h_proto = htons(0x88A4);
+ memset(eth->h_dest, 0xFF, ETH_ALEN);
+ }
+
+ return 0;
+
+out_tx_ring:
+ for (i = 0; i < EC_TX_RING_SIZE; i++) {
+ if (device->tx_skb[i]) {
+ dev_kfree_skb(device->tx_skb[i]);
+ }
+ }
+#ifdef EC_DEBUG_IF
+ ec_debug_clear(&device->dbg);
+out_return:
+#endif
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_device_clear(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ unsigned int i;
+
+ if (device->open) {
+ ec_device_close(device);
+ }
+ for (i = 0; i < EC_TX_RING_SIZE; i++)
+ dev_kfree_skb(device->tx_skb[i]);
+#ifdef EC_DEBUG_IF
+ ec_debug_clear(&device->dbg);
+#endif
+}
+
+/*****************************************************************************/
+
+/** Associate with net_device.
+ */
+void ec_device_attach(
+ ec_device_t *device, /**< EtherCAT device */
+ struct net_device *net_dev, /**< net_device structure */
+ ec_pollfunc_t poll, /**< pointer to device's poll function */
+ struct module *module /**< the device's module */
+ )
+{
+ unsigned int i;
+ struct ethhdr *eth;
+
+ ec_device_detach(device); // resets fields
+
+ device->dev = net_dev;
+ device->poll = poll;
+ device->module = module;
+
+ for (i = 0; i < EC_TX_RING_SIZE; i++) {
+ device->tx_skb[i]->dev = net_dev;
+ eth = (struct ethhdr *) (device->tx_skb[i]->data);
+ memcpy(eth->h_source, net_dev->dev_addr, ETH_ALEN);
+ }
+
+#ifdef EC_DEBUG_IF
+ ec_debug_register(&device->dbg, net_dev);
+#endif
+}
+
+/*****************************************************************************/
+
+/** Disconnect from net_device.
+ */
+void ec_device_detach(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ unsigned int i;
+
+#ifdef EC_DEBUG_IF
+ ec_debug_unregister(&device->dbg);
+#endif
+
+ device->dev = NULL;
+ device->poll = NULL;
+ device->module = NULL;
+ device->open = 0;
+ device->link_state = 0; // down
+
+ ec_device_clear_stats(device);
+
+ for (i = 0; i < EC_TX_RING_SIZE; i++) {
+ device->tx_skb[i]->dev = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/** Opens the EtherCAT device.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_device_open(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ int ret;
+
+ if (!device->dev) {
+ EC_MASTER_ERR(device->master, "No net_device to open!\n");
+ return -ENODEV;
+ }
+
+ if (device->open) {
+ EC_MASTER_WARN(device->master, "Device already opened!\n");
+ return 0;
+ }
+
+ device->link_state = 0;
+
+ ec_device_clear_stats(device);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ rtnl_lock();
+ ret = device->dev->netdev_ops->ndo_open(device->dev);
+ rtnl_unlock();
+#else
+ ret = device->dev->open(device->dev);
+#endif
+ if (!ret)
+ device->open = 1;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Stops the EtherCAT device.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_device_close(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ int ret;
+
+ if (!device->dev) {
+ EC_MASTER_ERR(device->master, "No device to close!\n");
+ return -ENODEV;
+ }
+
+ if (!device->open) {
+ EC_MASTER_WARN(device->master, "Device already closed!\n");
+ return 0;
+ }
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ ret = device->dev->netdev_ops->ndo_stop(device->dev);
+#else
+ ret = device->dev->stop(device->dev);
+#endif
+ if (!ret)
+ device->open = 0;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Returns a pointer to the device's transmit memory.
+ *
+ * \return pointer to the TX socket buffer
+ */
+uint8_t *ec_device_tx_data(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ /* cycle through socket buffers, because otherwise there is a race
+ * condition, if multiple frames are sent and the DMA is not scheduled in
+ * between. */
+ device->tx_ring_index++;
+ device->tx_ring_index %= EC_TX_RING_SIZE;
+ return device->tx_skb[device->tx_ring_index]->data + ETH_HLEN;
+}
+
+/*****************************************************************************/
+
+/** Sends the content of the transmit socket buffer.
+ *
+ * Cuts the socket buffer content to the (now known) size, and calls the
+ * start_xmit() function of the assigned net_device.
+ */
+void ec_device_send(
+ ec_device_t *device, /**< EtherCAT device */
+ size_t size /**< number of bytes to send */
+ )
+{
+ struct sk_buff *skb = device->tx_skb[device->tx_ring_index];
+
+ // set the right length for the data
+ skb->len = ETH_HLEN + size;
+
+ if (unlikely(device->master->debug_level > 1)) {
+ EC_MASTER_DBG(device->master, 2, "Sending frame:\n");
+ ec_print_data(skb->data, ETH_HLEN + size);
+ }
+
+ // start sending
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ if (device->dev->netdev_ops->ndo_start_xmit(skb, device->dev) ==
+ NETDEV_TX_OK)
+#else
+ if (device->dev->hard_start_xmit(skb, device->dev) == NETDEV_TX_OK)
+#endif
+ {
+ device->tx_count++;
+ device->master->device_stats.tx_count++;
+ device->tx_bytes += ETH_HLEN + size;
+ device->master->device_stats.tx_bytes += ETH_HLEN + size;
+#ifdef EC_DEBUG_IF
+ ec_debug_send(&device->dbg, skb->data, ETH_HLEN + size);
+#endif
+#ifdef EC_DEBUG_RING
+ ec_device_debug_ring_append(
+ device, TX, skb->data + ETH_HLEN, size);
+#endif
+ } else {
+ device->tx_errors++;
+ }
+}
+
+/*****************************************************************************/
+
+/** Clears the frame statistics.
+ */
+void ec_device_clear_stats(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ unsigned int i;
+
+ // zero frame statistics
+ device->tx_count = 0;
+ device->last_tx_count = 0;
+ device->rx_count = 0;
+ device->last_rx_count = 0;
+ device->tx_bytes = 0;
+ device->last_tx_bytes = 0;
+ device->rx_bytes = 0;
+ device->last_rx_bytes = 0;
+ device->tx_errors = 0;
+
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ device->tx_frame_rates[i] = 0;
+ device->rx_frame_rates[i] = 0;
+ device->tx_byte_rates[i] = 0;
+ device->rx_byte_rates[i] = 0;
+ }
+}
+
+/*****************************************************************************/
+
+#ifdef EC_DEBUG_RING
+/** Appends frame data to the debug ring.
+ */
+void ec_device_debug_ring_append(
+ ec_device_t *device, /**< EtherCAT device */
+ ec_debug_frame_dir_t dir, /**< direction */
+ const void *data, /**< frame data */
+ size_t size /**< data size */
+ )
+{
+ ec_debug_frame_t *df = &device->debug_frames[device->debug_frame_index];
+
+ df->dir = dir;
+ if (dir == TX) {
+ do_gettimeofday(&df->t);
+ }
+ else {
+ df->t = device->timeval_poll;
+ }
+ memcpy(df->data, data, size);
+ df->data_size = size;
+
+ device->debug_frame_index++;
+ device->debug_frame_index %= EC_DEBUG_RING_SIZE;
+ if (unlikely(device->debug_frame_count < EC_DEBUG_RING_SIZE))
+ device->debug_frame_count++;
+}
+
+/*****************************************************************************/
+
+/** Outputs the debug ring.
+ */
+void ec_device_debug_ring_print(
+ const ec_device_t *device /**< EtherCAT device */
+ )
+{
+ int i;
+ unsigned int ring_index;
+ const ec_debug_frame_t *df;
+ struct timeval t0, diff;
+
+ // calculate index of the newest frame in the ring to get its time
+ ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE - 1)
+ % EC_DEBUG_RING_SIZE;
+ t0 = device->debug_frames[ring_index].t;
+
+ EC_MASTER_DBG(device->master, 1, "Debug ring %u:\n", ring_index);
+
+ // calculate index of the oldest frame in the ring
+ ring_index = (device->debug_frame_index + EC_DEBUG_RING_SIZE
+ - device->debug_frame_count) % EC_DEBUG_RING_SIZE;
+
+ for (i = 0; i < device->debug_frame_count; i++) {
+ df = &device->debug_frames[ring_index];
+ timersub(&t0, &df->t, &diff);
+
+ EC_MASTER_DBG(device->master, 1, "Frame %u, dt=%u.%06u s, %s:\n",
+ i + 1 - device->debug_frame_count,
+ (unsigned int) diff.tv_sec,
+ (unsigned int) diff.tv_usec,
+ (df->dir == TX) ? "TX" : "RX");
+ ec_print_data(df->data, df->data_size);
+
+ ring_index++;
+ ring_index %= EC_DEBUG_RING_SIZE;
+ }
+}
+#endif
+
+/*****************************************************************************/
+
+/** Calls the poll function of the assigned net_device.
+ *
+ * The master itself works without using interrupts. Therefore the processing
+ * of received data and status changes of the network device has to be
+ * done by the master calling the ISR "manually".
+ */
+void ec_device_poll(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+#ifdef EC_HAVE_CYCLES
+ device->cycles_poll = get_cycles();
+#endif
+ device->jiffies_poll = jiffies;
+#ifdef EC_DEBUG_RING
+ do_gettimeofday(&device->timeval_poll);
+#endif
+ device->poll(device->dev);
+}
+
+/*****************************************************************************/
+
+/** Update device statistics.
+ */
+void ec_device_update_stats(
+ ec_device_t *device /**< EtherCAT device */
+ )
+{
+ unsigned int i;
+
+ s32 tx_frame_rate = (device->tx_count - device->last_tx_count) * 1000;
+ s32 rx_frame_rate = (device->rx_count - device->last_rx_count) * 1000;
+ s32 tx_byte_rate = (device->tx_bytes - device->last_tx_bytes);
+ s32 rx_byte_rate = (device->rx_bytes - device->last_rx_bytes);
+
+ /* Low-pass filter:
+ * Y_n = y_(n - 1) + T / tau * (x - y_(n - 1)) | T = 1
+ * -> Y_n += (x - y_(n - 1)) / tau
+ */
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ s32 n = rate_intervals[i];
+ device->tx_frame_rates[i] +=
+ (tx_frame_rate - device->tx_frame_rates[i]) / n;
+ device->rx_frame_rates[i] +=
+ (rx_frame_rate - device->rx_frame_rates[i]) / n;
+ device->tx_byte_rates[i] +=
+ (tx_byte_rate - device->tx_byte_rates[i]) / n;
+ device->rx_byte_rates[i] +=
+ (rx_byte_rate - device->rx_byte_rates[i]) / n;
+ }
+
+ device->last_tx_count = device->tx_count;
+ device->last_rx_count = device->rx_count;
+ device->last_tx_bytes = device->tx_bytes;
+ device->last_rx_bytes = device->rx_bytes;
+}
+
+/******************************************************************************
+ * Device interface
+ *****************************************************************************/
+
+/** Withdraws an EtherCAT device from the master.
+ *
+ * The device is disconnected from the master and all device ressources
+ * are freed.
+ *
+ * \attention Before calling this function, the ecdev_stop() function has
+ * to be called, to be sure that the master does not use the device
+ * any more.
+ * \ingroup DeviceInterface
+ */
+void ecdev_withdraw(ec_device_t *device /**< EtherCAT device */)
+{
+ ec_master_t *master = device->master;
+ char dev_str[20], mac_str[20];
+
+ ec_mac_print(device->dev->dev_addr, mac_str);
+
+ if (device == &master->devices[EC_DEVICE_MAIN]) {
+ sprintf(dev_str, "main");
+ } else if (device == &master->devices[EC_DEVICE_BACKUP]) {
+ sprintf(dev_str, "backup");
+ } else {
+ EC_MASTER_WARN(master, "%s() called with unknown device %s!\n",
+ __func__, mac_str);
+ sprintf(dev_str, "UNKNOWN");
+ }
+
+ EC_MASTER_INFO(master, "Releasing %s device %s.\n", dev_str, mac_str);
+
+ down(&master->device_sem);
+ ec_device_detach(device);
+ up(&master->device_sem);
+}
+
+/*****************************************************************************/
+
+/** Opens the network device and makes the master enter IDLE phase.
+ *
+ * \return 0 on success, else < 0
+ * \ingroup DeviceInterface
+ */
+int ecdev_open(ec_device_t *device /**< EtherCAT device */)
+{
+ int ret;
+ ec_master_t *master = device->master;
+ unsigned int all_open = 1, dev_idx;
+
+ ret = ec_device_open(device);
+ if (ret) {
+ EC_MASTER_ERR(master, "Failed to open device!\n");
+ return ret;
+ }
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(device->master); dev_idx++) {
+ if (!master->devices[dev_idx].open) {
+ all_open = 0;
+ break;
+ }
+ }
+
+ if (all_open) {
+ ret = ec_master_enter_idle_phase(device->master);
+ if (ret) {
+ EC_MASTER_ERR(device->master, "Failed to enter IDLE phase!\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Makes the master leave IDLE phase and closes the network device.
+ *
+ * \return 0 on success, else < 0
+ * \ingroup DeviceInterface
+ */
+void ecdev_close(ec_device_t *device /**< EtherCAT device */)
+{
+ ec_master_t *master = device->master;
+
+ if (master->phase == EC_IDLE) {
+ ec_master_leave_idle_phase(master);
+ }
+
+ if (ec_device_close(device)) {
+ EC_MASTER_WARN(master, "Failed to close device!\n");
+ }
+}
+
+/*****************************************************************************/
+
+/** Accepts a received frame.
+ *
+ * Forwards the received data to the master. The master will analyze the frame
+ * and dispatch the received commands to the sending instances.
+ *
+ * The data have to begin with the Ethernet header (target MAC address).
+ *
+ * \ingroup DeviceInterface
+ */
+void ecdev_receive(
+ ec_device_t *device, /**< EtherCAT device */
+ const void *data, /**< pointer to received data */
+ size_t size /**< number of bytes received */
+ )
+{
+ const void *ec_data = data + ETH_HLEN;
+ size_t ec_size = size - ETH_HLEN;
+
+ if (unlikely(!data)) {
+ EC_MASTER_WARN(device->master, "%s() called with NULL data.\n",
+ __func__);
+ return;
+ }
+
+ device->rx_count++;
+ device->master->device_stats.rx_count++;
+ device->rx_bytes += size;
+ device->master->device_stats.rx_bytes += size;
+
+ if (unlikely(device->master->debug_level > 1)) {
+ EC_MASTER_DBG(device->master, 2, "Received frame:\n");
+ ec_print_data(data, size);
+ }
+ //u8 *pointer = ec_data;
+ //pr_info("ec data %02x", pointer[0]);
+ //pr_info("ec data %02x %02x %02x %02x", pointer[0], pointer[1], pointer[2], pointer[3]);
+
+#ifdef EC_DEBUG_IF
+ ec_debug_send(&device->dbg, data, size);
+#endif
+#ifdef EC_DEBUG_RING
+ ec_device_debug_ring_append(device, RX, ec_data, ec_size);
+#endif
+
+ ec_master_receive_datagrams(device->master, device, ec_data, ec_size);
+}
+
+/*****************************************************************************/
+
+/** Sets a new link state.
+ *
+ * If the device notifies the master about the link being down, the master
+ * will not try to send frames using this device.
+ *
+ * \ingroup DeviceInterface
+ */
+void ecdev_set_link(
+ ec_device_t *device, /**< EtherCAT device */
+ uint8_t state /**< new link state */
+ )
+{
+ if (unlikely(!device)) {
+ EC_WARN("ecdev_set_link() called with null device!\n");
+ return;
+ }
+
+ if (likely(state != device->link_state)) {
+ device->link_state = state;
+ EC_MASTER_INFO(device->master,
+ "Link state of %s changed to %s.\n",
+ device->dev->name, (state ? "UP" : "DOWN"));
+ }
+}
+
+/*****************************************************************************/
+
+/** Reads the link state.
+ *
+ * \ingroup DeviceInterface
+ *
+ * \return Link state.
+ */
+uint8_t ecdev_get_link(
+ const ec_device_t *device /**< EtherCAT device */
+ )
+{
+ if (unlikely(!device)) {
+ EC_WARN("ecdev_get_link() called with null device!\n");
+ return 0;
+ }
+
+ return device->link_state;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecdev_withdraw);
+EXPORT_SYMBOL(ecdev_open);
+EXPORT_SYMBOL(ecdev_close);
+EXPORT_SYMBOL(ecdev_receive);
+EXPORT_SYMBOL(ecdev_get_link);
+EXPORT_SYMBOL(ecdev_set_link);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/device.h b/net/ethercat/master/device.h
new file mode 100644
index 000000000000..2c26e2b2656c
--- /dev/null
+++ b/net/ethercat/master/device.h
@@ -0,0 +1,158 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 device structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DEVICE_H__
+#define __EC_DEVICE_H__
+
+#include <linux/interrupt.h>
+
+#include <net/ecdev.h>
+#include "globals.h"
+
+/**
+ * Size of the transmit ring.
+ * This memory ring is used to transmit frames. It is necessary to use
+ * different memory regions, because otherwise the network device DMA could
+ * send the same data twice, if it is called twice.
+ */
+#define EC_TX_RING_SIZE 2
+
+#ifdef EC_DEBUG_IF
+#include "debug.h"
+#endif
+
+#ifdef EC_DEBUG_RING
+#define EC_DEBUG_RING_SIZE 10
+
+typedef enum {
+ TX, RX
+} ec_debug_frame_dir_t;
+
+typedef struct {
+ ec_debug_frame_dir_t dir;
+ struct timeval t;
+ uint8_t data[EC_MAX_DATA_SIZE];
+ unsigned int data_size;
+} ec_debug_frame_t;
+
+#endif
+
+/*****************************************************************************/
+
+/**
+ EtherCAT device.
+ An EtherCAT device is a network interface card, that is owned by an
+ EtherCAT master to send and receive EtherCAT frames with.
+*/
+
+struct ec_device
+{
+ ec_master_t *master; /**< EtherCAT master */
+ struct net_device *dev; /**< pointer to the assigned net_device */
+ ec_pollfunc_t poll; /**< pointer to the device's poll function */
+ struct module *module; /**< pointer to the device's owning module */
+ uint8_t open; /**< true, if the net_device has been opened */
+ uint8_t link_state; /**< device link state */
+ struct sk_buff *tx_skb[EC_TX_RING_SIZE]; /**< transmit skb ring */
+ unsigned int tx_ring_index; /**< last ring entry used to transmit */
+#ifdef EC_HAVE_CYCLES
+ cycles_t cycles_poll; /**< cycles of last poll */
+#endif
+#ifdef EC_DEBUG_RING
+ struct timeval timeval_poll;
+#endif
+ unsigned long jiffies_poll; /**< jiffies of last poll */
+
+ // Frame statistics
+ u64 tx_count; /**< Number of frames sent. */
+ u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
+ u64 rx_count; /**< Number of frames received. */
+ u64 last_rx_count; /**< Number of frames received of last statistics
+ cycle. */
+ u64 tx_bytes; /**< Number of bytes sent. */
+ u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+ u64 rx_bytes; /**< Number of bytes received. */
+ u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+ */
+ u64 tx_errors; /**< Number of transmit errors. */
+ s32 tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s for
+ different statistics cycle periods. */
+ s32 rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s for
+ different statistics cycle periods. */
+
+#ifdef EC_DEBUG_IF
+ ec_debug_t dbg; /**< debug device */
+#endif
+#ifdef EC_DEBUG_RING
+ ec_debug_frame_t debug_frames[EC_DEBUG_RING_SIZE];
+ unsigned int debug_frame_index;
+ unsigned int debug_frame_count;
+#endif
+};
+
+/*****************************************************************************/
+
+int ec_device_init(ec_device_t *, ec_master_t *);
+void ec_device_clear(ec_device_t *);
+
+void ec_device_attach(ec_device_t *, struct net_device *, ec_pollfunc_t,
+ struct module *);
+void ec_device_detach(ec_device_t *);
+
+int ec_device_open(ec_device_t *);
+int ec_device_close(ec_device_t *);
+
+void ec_device_poll(ec_device_t *);
+uint8_t *ec_device_tx_data(ec_device_t *);
+void ec_device_send(ec_device_t *, size_t);
+void ec_device_clear_stats(ec_device_t *);
+void ec_device_update_stats(ec_device_t *);
+
+#ifdef EC_DEBUG_RING
+void ec_device_debug_ring_append(ec_device_t *, ec_debug_frame_dir_t,
+ const void *, size_t);
+void ec_device_debug_ring_print(const ec_device_t *);
+#endif
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/domain.c b/net/ethercat/master/domain.c
new file mode 100644
index 000000000000..a1ec9b8cf370
--- /dev/null
+++ b/net/ethercat/master/domain.c
@@ -0,0 +1,717 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 domain methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include "globals.h"
+#include "master.h"
+#include "slave_config.h"
+
+#include "domain.h"
+#include "datagram_pair.h"
+
+/** Extra debug output for redundancy functions.
+ */
+#define DEBUG_REDUNDANCY 0
+
+/*****************************************************************************/
+
+void ec_domain_clear_data(ec_domain_t *);
+
+/*****************************************************************************/
+
+/** Domain constructor.
+ */
+void ec_domain_init(
+ ec_domain_t *domain, /**< EtherCAT domain. */
+ ec_master_t *master, /**< Parent master. */
+ unsigned int index /**< Index. */
+ )
+{
+ unsigned int dev_idx;
+
+ domain->master = master;
+ domain->index = index;
+ INIT_LIST_HEAD(&domain->fmmu_configs);
+ domain->data_size = 0;
+ domain->data = NULL;
+ domain->data_origin = EC_ORIG_INTERNAL;
+ domain->logical_base_address = 0x00000000;
+ INIT_LIST_HEAD(&domain->datagram_pairs);
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ domain->working_counter[dev_idx] = 0x0000;
+ }
+ domain->expected_working_counter = 0x0000;
+ domain->working_counter_changes = 0;
+ domain->redundancy_active = 0;
+ domain->notify_jiffies = 0;
+}
+
+/*****************************************************************************/
+
+/** Domain destructor.
+ */
+void ec_domain_clear(ec_domain_t *domain /**< EtherCAT domain */)
+{
+ ec_datagram_pair_t *datagram_pair, *next_pair;
+
+ // dequeue and free datagrams
+ list_for_each_entry_safe(datagram_pair, next_pair,
+ &domain->datagram_pairs, list) {
+ ec_datagram_pair_clear(datagram_pair);
+ kfree(datagram_pair);
+ }
+
+ ec_domain_clear_data(domain);
+}
+
+/*****************************************************************************/
+
+/** Frees internally allocated memory.
+ */
+void ec_domain_clear_data(
+ ec_domain_t *domain /**< EtherCAT domain. */
+ )
+{
+ if (domain->data_origin == EC_ORIG_INTERNAL && domain->data) {
+ kfree(domain->data);
+ }
+
+ domain->data = NULL;
+ domain->data_origin = EC_ORIG_INTERNAL;
+}
+
+/*****************************************************************************/
+
+/** Adds an FMMU configuration to the domain.
+ */
+void ec_domain_add_fmmu_config(
+ ec_domain_t *domain, /**< EtherCAT domain. */
+ ec_fmmu_config_t *fmmu /**< FMMU configuration. */
+ )
+{
+ fmmu->domain = domain;
+
+ domain->data_size += fmmu->data_size;
+ list_add_tail(&fmmu->list, &domain->fmmu_configs);
+
+ EC_MASTER_DBG(domain->master, 1, "Domain %u:"
+ " Added %u bytes, total %zu.\n",
+ domain->index, fmmu->data_size, domain->data_size);
+}
+
+/*****************************************************************************/
+
+/** Allocates a domain datagram pair and appends it to the list.
+ *
+ * The datagrams' types and expected working counters are determined by the
+ * number of input and output fmmus that share the datagrams.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_domain_add_datagram_pair(
+ ec_domain_t *domain, /**< EtherCAT domain. */
+ uint32_t logical_offset, /**< Logical offset. */
+ size_t data_size, /**< Size of the data. */
+ uint8_t *data, /**< Process data. */
+ const unsigned int used[] /**< Slave config counter for in/out. */
+ )
+{
+ ec_datagram_pair_t *datagram_pair;
+ int ret;
+
+ if (!(datagram_pair = kmalloc(sizeof(ec_datagram_pair_t), GFP_KERNEL))) {
+ EC_MASTER_ERR(domain->master,
+ "Failed to allocate domain datagram pair!\n");
+ return -ENOMEM;
+ }
+
+ ret = ec_datagram_pair_init(datagram_pair, domain, logical_offset, data,
+ data_size, used);
+ if (ret) {
+ kfree(datagram_pair);
+ return ret;
+ }
+
+ domain->expected_working_counter +=
+ datagram_pair->expected_working_counter;
+
+ EC_MASTER_DBG(domain->master, 1,
+ "Adding datagram pair with expected WC %u.\n",
+ datagram_pair->expected_working_counter);
+
+
+ list_add_tail(&datagram_pair->list, &domain->datagram_pairs);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Domain finish helper function.
+ *
+ * Detects, if a slave configuration has already been taken into account for
+ * a datagram's expected working counter calculation.
+ *
+ * Walks through the list of all FMMU configurations for the current datagram
+ * and ends before the current datagram.
+ *
+ * \return Non-zero if slave connfig was already counted.
+ */
+int shall_count(
+ const ec_fmmu_config_t *cur_fmmu, /**< Current FMMU with direction to
+ search for. */
+ const ec_fmmu_config_t *first_fmmu /**< Datagram's first FMMU. */
+ )
+{
+ for (; first_fmmu != cur_fmmu;
+ first_fmmu = list_entry(first_fmmu->list.next,
+ ec_fmmu_config_t, list)) {
+
+ if (first_fmmu->sc == cur_fmmu->sc
+ && first_fmmu->dir == cur_fmmu->dir) {
+ return 0; // was already counted
+ }
+ }
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Finishes a domain.
+ *
+ * This allocates the necessary datagrams and writes the correct logical
+ * addresses to every configured FMMU.
+ *
+ * \todo Check for FMMUs that do not fit into any datagram.
+ *
+ * \retval 0 Success
+ * \retval <0 Error code.
+ */
+int ec_domain_finish(
+ ec_domain_t *domain, /**< EtherCAT domain. */
+ uint32_t base_address /**< Logical base address. */
+ )
+{
+ uint32_t datagram_offset;
+ size_t datagram_size;
+ unsigned int datagram_count;
+ unsigned int datagram_used[EC_DIR_COUNT];
+ ec_fmmu_config_t *fmmu;
+ const ec_fmmu_config_t *datagram_first_fmmu = NULL;
+ const ec_datagram_pair_t *datagram_pair;
+ int ret;
+
+ domain->logical_base_address = base_address;
+
+ if (domain->data_size && domain->data_origin == EC_ORIG_INTERNAL) {
+ if (!(domain->data =
+ (uint8_t *) kmalloc(domain->data_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(domain->master, "Failed to allocate %zu bytes"
+ " internal memory for domain %u!\n",
+ domain->data_size, domain->index);
+ return -ENOMEM;
+ }
+ }
+
+ // Cycle through all domain FMMUs and
+ // - correct the logical base addresses
+ // - set up the datagrams to carry the process data
+ // - calculate the datagrams' expected working counters
+ datagram_offset = 0;
+ datagram_size = 0;
+ datagram_count = 0;
+ datagram_used[EC_DIR_OUTPUT] = 0;
+ datagram_used[EC_DIR_INPUT] = 0;
+
+ if (!list_empty(&domain->fmmu_configs)) {
+ datagram_first_fmmu =
+ list_entry(domain->fmmu_configs.next, ec_fmmu_config_t, list);
+ }
+
+ list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
+
+ // Correct logical FMMU address
+ fmmu->logical_start_address += base_address;
+
+ // If the current FMMU's data do not fit in the current datagram,
+ // allocate a new one.
+ if (datagram_size + fmmu->data_size > EC_MAX_DATA_SIZE) {
+ ret = ec_domain_add_datagram_pair(domain,
+ domain->logical_base_address + datagram_offset,
+ datagram_size, domain->data + datagram_offset,
+ datagram_used);
+ if (ret < 0)
+ return ret;
+
+ datagram_offset += datagram_size;
+ datagram_size = 0;
+ datagram_count++;
+ datagram_used[EC_DIR_OUTPUT] = 0;
+ datagram_used[EC_DIR_INPUT] = 0;
+ datagram_first_fmmu = fmmu;
+ }
+
+ // Increment Input/Output counter to determine datagram types
+ // and calculate expected working counters
+ if (shall_count(fmmu, datagram_first_fmmu)) {
+ datagram_used[fmmu->dir]++;
+ }
+
+ datagram_size += fmmu->data_size;
+ }
+
+ /* Allocate last datagram pair, if data are left (this is also the case if
+ * the process data fit into a single datagram) */
+ if (datagram_size) {
+ ret = ec_domain_add_datagram_pair(domain,
+ domain->logical_base_address + datagram_offset,
+ datagram_size, domain->data + datagram_offset,
+ datagram_used);
+ if (ret < 0)
+ return ret;
+ datagram_count++;
+ }
+
+ EC_MASTER_INFO(domain->master, "Domain%u: Logical address 0x%08x,"
+ " %zu byte, expected working counter %u.\n", domain->index,
+ domain->logical_base_address, domain->data_size,
+ domain->expected_working_counter);
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+ const ec_datagram_t *datagram =
+ &datagram_pair->datagrams[EC_DEVICE_MAIN];
+ EC_MASTER_INFO(domain->master, " Datagram %s: Logical offset 0x%08x,"
+ " %zu byte, type %s.\n", datagram->name,
+ EC_READ_U32(datagram->address), datagram->data_size,
+ ec_datagram_type_string(datagram));
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the number of FMMU configurations of the domain.
+ */
+unsigned int ec_domain_fmmu_count(const ec_domain_t *domain)
+{
+ const ec_fmmu_config_t *fmmu;
+ unsigned int num = 0;
+
+ list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
+ num++;
+ }
+
+ return num;
+}
+
+/*****************************************************************************/
+
+/** Get a certain FMMU configuration via its position in the list.
+ *
+ * \return FMMU at position \a pos, or NULL.
+ */
+const ec_fmmu_config_t *ec_domain_find_fmmu(
+ const ec_domain_t *domain, /**< EtherCAT domain. */
+ unsigned int pos /**< List position. */
+ )
+{
+ const ec_fmmu_config_t *fmmu;
+
+ list_for_each_entry(fmmu, &domain->fmmu_configs, list) {
+ if (pos--)
+ continue;
+ return fmmu;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+#if EC_MAX_NUM_DEVICES > 1
+
+/** Process received data.
+ */
+int data_changed(
+ uint8_t *send_buffer,
+ const ec_datagram_t *datagram,
+ size_t offset,
+ size_t size
+ )
+{
+ uint8_t *sent = send_buffer + offset;
+ uint8_t *recv = datagram->data + offset;
+ size_t i;
+
+ for (i = 0; i < size; i++) {
+ if (recv[i] != sent[i]) {
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+#endif
+
+/******************************************************************************
+ * Application interface
+ *****************************************************************************/
+
+int ecrt_domain_reg_pdo_entry_list(ec_domain_t *domain,
+ const ec_pdo_entry_reg_t *regs)
+{
+ const ec_pdo_entry_reg_t *reg;
+ ec_slave_config_t *sc;
+ int ret;
+
+ EC_MASTER_DBG(domain->master, 1, "ecrt_domain_reg_pdo_entry_list("
+ "domain = 0x%p, regs = 0x%p)\n", domain, regs);
+
+ for (reg = regs; reg->index; reg++) {
+ sc = ecrt_master_slave_config_err(domain->master, reg->alias,
+ reg->position, reg->vendor_id, reg->product_code);
+ if (IS_ERR(sc))
+ return PTR_ERR(sc);
+
+ ret = ecrt_slave_config_reg_pdo_entry(sc, reg->index,
+ reg->subindex, domain, reg->bit_position);
+ if (ret < 0)
+ return ret;
+
+ *reg->offset = ret;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+size_t ecrt_domain_size(const ec_domain_t *domain)
+{
+ return domain->data_size;
+}
+
+/*****************************************************************************/
+
+void ecrt_domain_external_memory(ec_domain_t *domain, uint8_t *mem)
+{
+ EC_MASTER_DBG(domain->master, 1, "ecrt_domain_external_memory("
+ "domain = 0x%p, mem = 0x%p)\n", domain, mem);
+
+ down(&domain->master->master_sem);
+
+ ec_domain_clear_data(domain);
+
+ domain->data = mem;
+ domain->data_origin = EC_ORIG_EXTERNAL;
+
+ up(&domain->master->master_sem);
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_domain_data(ec_domain_t *domain)
+{
+ return domain->data;
+}
+
+/*****************************************************************************/
+
+void ecrt_domain_process(ec_domain_t *domain)
+{
+ uint16_t wc_sum[EC_MAX_NUM_DEVICES] = {}, wc_total;
+ ec_datagram_pair_t *pair;
+#if EC_MAX_NUM_DEVICES > 1
+ uint16_t datagram_pair_wc, redundant_wc;
+ unsigned int datagram_offset;
+ ec_fmmu_config_t *fmmu = list_first_entry(&domain->fmmu_configs,
+ ec_fmmu_config_t, list);
+ unsigned int redundancy;
+#endif
+ unsigned int dev_idx;
+#ifdef EC_RT_SYSLOG
+ unsigned int wc_change;
+#endif
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "domain %u process\n", domain->index);
+#endif
+
+ list_for_each_entry(pair, &domain->datagram_pairs, list) {
+#if EC_MAX_NUM_DEVICES > 1
+ datagram_pair_wc = ec_datagram_pair_process(pair, wc_sum);
+#else
+ ec_datagram_pair_process(pair, wc_sum);
+#endif
+
+#if EC_MAX_NUM_DEVICES > 1
+ if (ec_master_num_devices(domain->master) > 1) {
+ ec_datagram_t *main_datagram = &pair->datagrams[EC_DEVICE_MAIN];
+ uint32_t logical_datagram_address =
+ EC_READ_U32(main_datagram->address);
+ size_t datagram_size = main_datagram->data_size;
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "dgram %s log=%u\n",
+ main_datagram->name, logical_datagram_address);
+#endif
+
+ /* Redundancy: Go through FMMU configs to detect data changes. */
+ list_for_each_entry_from(fmmu, &domain->fmmu_configs, list) {
+ ec_datagram_t *backup_datagram =
+ &pair->datagrams[EC_DEVICE_BACKUP];
+
+ if (fmmu->dir != EC_DIR_INPUT) {
+ continue;
+ }
+
+ if (fmmu->logical_start_address >=
+ logical_datagram_address + datagram_size) {
+ // fmmu data contained in next datagram pair
+ break;
+ }
+
+ datagram_offset =
+ fmmu->logical_start_address - logical_datagram_address;
+
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1,
+ "input fmmu log=%u size=%u offset=%u\n",
+ fmmu->logical_start_address, fmmu->data_size,
+ datagram_offset);
+ if (domain->master->debug_level > 0) {
+ ec_print_data(pair->send_buffer + datagram_offset,
+ fmmu->data_size);
+ ec_print_data(main_datagram->data + datagram_offset,
+ fmmu->data_size);
+ ec_print_data(backup_datagram->data + datagram_offset,
+ fmmu->data_size);
+ }
+#endif
+
+ if (data_changed(pair->send_buffer, main_datagram,
+ datagram_offset, fmmu->data_size)) {
+ /* data changed on main link: no copying necessary. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "main changed\n");
+#endif
+ } else if (data_changed(pair->send_buffer, backup_datagram,
+ datagram_offset, fmmu->data_size)) {
+ /* data changed on backup link: copy to main memory. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1, "backup changed\n");
+#endif
+ memcpy(main_datagram->data + datagram_offset,
+ backup_datagram->data + datagram_offset,
+ fmmu->data_size);
+ } else if (datagram_pair_wc ==
+ pair->expected_working_counter) {
+ /* no change, but WC complete: use main data. */
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1,
+ "no change but complete\n");
+#endif
+ } else {
+ /* no change and WC incomplete: mark WC as zero to avoid
+ * data.dependent WC flickering. */
+ datagram_pair_wc = 0;
+#if DEBUG_REDUNDANCY
+ EC_MASTER_DBG(domain->master, 1,
+ "no change and incomplete\n");
+#endif
+ }
+ }
+ }
+#endif // EC_MAX_NUM_DEVICES > 1
+ }
+
+#if EC_MAX_NUM_DEVICES > 1
+ redundant_wc = 0;
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ redundant_wc += wc_sum[dev_idx];
+ }
+
+ redundancy = redundant_wc > 0;
+ if (redundancy != domain->redundancy_active) {
+#ifdef EC_RT_SYSLOG
+ if (redundancy) {
+ EC_MASTER_WARN(domain->master,
+ "Domain %u: Redundant link in use!\n",
+ domain->index);
+ } else {
+ EC_MASTER_INFO(domain->master,
+ "Domain %u: Redundant link unused again.\n",
+ domain->index);
+ }
+#endif
+ domain->redundancy_active = redundancy;
+ }
+#else
+ domain->redundancy_active = 0;
+#endif
+
+#ifdef EC_RT_SYSLOG
+ wc_change = 0;
+#endif
+ wc_total = 0;
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ if (wc_sum[dev_idx] != domain->working_counter[dev_idx]) {
+#ifdef EC_RT_SYSLOG
+ wc_change = 1;
+#endif
+ domain->working_counter[dev_idx] = wc_sum[dev_idx];
+ }
+ wc_total += wc_sum[dev_idx];
+ }
+
+#ifdef EC_RT_SYSLOG
+ if (wc_change) {
+ domain->working_counter_changes++;
+ }
+
+ if (domain->working_counter_changes &&
+ jiffies - domain->notify_jiffies > HZ) {
+ domain->notify_jiffies = jiffies;
+ if (domain->working_counter_changes == 1) {
+ EC_MASTER_INFO(domain->master, "Domain %u: Working counter"
+ " changed to %u/%u", domain->index,
+ wc_total, domain->expected_working_counter);
+ } else {
+ EC_MASTER_INFO(domain->master, "Domain %u: %u working counter"
+ " changes - now %u/%u", domain->index,
+ domain->working_counter_changes,
+ wc_total, domain->expected_working_counter);
+ }
+#if EC_MAX_NUM_DEVICES > 1
+ if (ec_master_num_devices(domain->master) > 1) {
+ printk(" (");
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master);
+ dev_idx++) {
+ printk("%u", domain->working_counter[dev_idx]);
+ if (dev_idx + 1 < ec_master_num_devices(domain->master)) {
+ printk("+");
+ }
+ }
+ printk(")");
+ }
+#endif
+ printk(".\n");
+
+ domain->working_counter_changes = 0;
+ }
+#endif
+}
+
+/*****************************************************************************/
+
+void ecrt_domain_queue(ec_domain_t *domain)
+{
+ ec_datagram_pair_t *datagram_pair;
+ ec_device_index_t dev_idx;
+
+ list_for_each_entry(datagram_pair, &domain->datagram_pairs, list) {
+
+#if EC_MAX_NUM_DEVICES > 1
+ /* copy main data to send buffer */
+ memcpy(datagram_pair->send_buffer,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+#endif
+ ec_master_queue_datagram(domain->master,
+ &datagram_pair->datagrams[EC_DEVICE_MAIN]);
+
+ /* copy main data to backup datagram */
+ for (dev_idx = EC_DEVICE_BACKUP;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ memcpy(datagram_pair->datagrams[dev_idx].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data,
+ datagram_pair->datagrams[EC_DEVICE_MAIN].data_size);
+ ec_master_queue_datagram(domain->master,
+ &datagram_pair->datagrams[dev_idx]);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
+{
+ unsigned int dev_idx;
+ uint16_t wc = 0;
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ wc += domain->working_counter[dev_idx];
+ }
+
+ state->working_counter = wc;
+
+ if (wc) {
+ if (wc == domain->expected_working_counter) {
+ state->wc_state = EC_WC_COMPLETE;
+ } else {
+ state->wc_state = EC_WC_INCOMPLETE;
+ }
+ } else {
+ state->wc_state = EC_WC_ZERO;
+ }
+
+ state->redundancy_active = domain->redundancy_active;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_domain_reg_pdo_entry_list);
+EXPORT_SYMBOL(ecrt_domain_size);
+EXPORT_SYMBOL(ecrt_domain_external_memory);
+EXPORT_SYMBOL(ecrt_domain_data);
+EXPORT_SYMBOL(ecrt_domain_process);
+EXPORT_SYMBOL(ecrt_domain_queue);
+EXPORT_SYMBOL(ecrt_domain_state);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/domain.h b/net/ethercat/master/domain.h
new file mode 100644
index 000000000000..1d15aaf2bb43
--- /dev/null
+++ b/net/ethercat/master/domain.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 domain structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_DOMAIN_H__
+#define __EC_DOMAIN_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "datagram.h"
+#include "master.h"
+#include "fmmu_config.h"
+
+/*****************************************************************************/
+
+/** EtherCAT domain.
+ *
+ * Handles the process data and the therefore needed datagrams of a certain
+ * group of slaves.
+ */
+struct ec_domain
+{
+ struct list_head list; /**< List item. */
+ ec_master_t *master; /**< EtherCAT master owning the domain. */
+ unsigned int index; /**< Index (just a number). */
+
+ struct list_head fmmu_configs; /**< FMMU configurations contained. */
+ size_t data_size; /**< Size of the process data. */
+ uint8_t *data; /**< Memory for the process data. */
+ ec_origin_t data_origin; /**< Origin of the \a data memory. */
+ uint32_t logical_base_address; /**< Logical offset address of the
+ process data. */
+ struct list_head datagram_pairs; /**< Datagrams pairs (main/backup) for
+ process data exchange. */
+ uint16_t working_counter[EC_MAX_NUM_DEVICES]; /**< Last working counter
+ values. */
+ uint16_t expected_working_counter; /**< Expected working counter. */
+ unsigned int working_counter_changes; /**< Working counter changes
+ since last notification. */
+ unsigned int redundancy_active; /**< Non-zero, if redundancy is in use. */
+ unsigned long notify_jiffies; /**< Time of last notification. */
+};
+
+/*****************************************************************************/
+
+void ec_domain_init(ec_domain_t *, ec_master_t *, unsigned int);
+void ec_domain_clear(ec_domain_t *);
+
+void ec_domain_add_fmmu_config(ec_domain_t *, ec_fmmu_config_t *);
+int ec_domain_finish(ec_domain_t *, uint32_t);
+
+unsigned int ec_domain_fmmu_count(const ec_domain_t *);
+const ec_fmmu_config_t *ec_domain_find_fmmu(const ec_domain_t *, unsigned int);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/doxygen.c b/net/ethercat/master/doxygen.c
new file mode 100644
index 000000000000..06832de90bd1
--- /dev/null
+++ b/net/ethercat/master/doxygen.c
@@ -0,0 +1,89 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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.
+ *
+ *****************************************************************************/
+
+// This file only contains the doxygen mainpage.
+
+/** \file
+ * Doxygen mainpage only.
+ */
+
+/*****************************************************************************/
+
+/**
+ \mainpage
+
+ \section sec_general General information
+
+ This HTML contains the complete code documentation.
+
+ The API documentations are in the <a href="modules.html">modules
+ section</a>.
+
+ For information how to build and install, see the INSTALL file in the source
+ root.
+
+ \section sec_contact Contact
+
+ \verbatim
+ Florian Pose <fp@igh-essen.com>
+ Ingenieurgemeinschaft IgH
+ Heinz-Baecker-Str. 34
+ D-45356 Essen
+ http://igh-essen.com
+ \endverbatim
+
+ \section sec_license License
+
+ \verbatim
+ Copyright (C) 2006-2013 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.
+ \endverbatim
+*/
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/ethernet.c b/net/ethercat/master/ethernet.c
new file mode 100644
index 000000000000..30de578ba926
--- /dev/null
+++ b/net/ethercat/master/ethernet.c
@@ -0,0 +1,874 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 (EoE).
+*/
+
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "globals.h"
+#include "master.h"
+#include "slave.h"
+#include "mailbox.h"
+#include "ethernet.h"
+
+/*****************************************************************************/
+
+/** Defines the debug level of EoE processing.
+ *
+ * 0 = No debug messages.
+ * 1 = Output warnings.
+ * 2 = Output actions.
+ * 3 = Output actions and frame data.
+ */
+#define EOE_DEBUG_LEVEL 1
+
+/** Size of the EoE tx queue.
+ */
+#define EC_EOE_TX_QUEUE_SIZE 100
+
+/** Number of tries.
+ */
+#define EC_EOE_TRIES 100
+
+/*****************************************************************************/
+
+void ec_eoe_flush(ec_eoe_t *);
+
+// state functions
+void ec_eoe_state_rx_start(ec_eoe_t *);
+void ec_eoe_state_rx_check(ec_eoe_t *);
+void ec_eoe_state_rx_fetch(ec_eoe_t *);
+void ec_eoe_state_tx_start(ec_eoe_t *);
+void ec_eoe_state_tx_sent(ec_eoe_t *);
+
+// net_device functions
+int ec_eoedev_open(struct net_device *);
+int ec_eoedev_stop(struct net_device *);
+int ec_eoedev_tx(struct sk_buff *, struct net_device *);
+struct net_device_stats *ec_eoedev_stats(struct net_device *);
+
+/*****************************************************************************/
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+/** Device operations for EoE interfaces.
+ */
+static const struct net_device_ops ec_eoedev_ops = {
+ .ndo_open = ec_eoedev_open,
+ .ndo_stop = ec_eoedev_stop,
+ .ndo_start_xmit = ec_eoedev_tx,
+ .ndo_get_stats = ec_eoedev_stats,
+};
+#endif
+
+/*****************************************************************************/
+
+/** EoE constructor.
+ *
+ * Initializes the EoE handler, creates a net_device and registers it.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_eoe_init(
+ ec_eoe_t *eoe, /**< EoE handler */
+ ec_slave_t *slave /**< EtherCAT slave */
+ )
+{
+ ec_eoe_t **priv;
+ int i, ret = 0;
+ char name[EC_DATAGRAM_NAME_SIZE];
+
+ eoe->slave = slave;
+
+ ec_datagram_init(&eoe->datagram);
+ eoe->queue_datagram = 0;
+ eoe->state = ec_eoe_state_rx_start;
+ eoe->opened = 0;
+ eoe->rx_skb = NULL;
+ eoe->rx_expected_fragment = 0;
+ INIT_LIST_HEAD(&eoe->tx_queue);
+ eoe->tx_frame = NULL;
+ eoe->tx_queue_active = 0;
+ eoe->tx_queue_size = EC_EOE_TX_QUEUE_SIZE;
+ eoe->tx_queued_frames = 0;
+
+ sema_init(&eoe->tx_queue_sem, 1);
+ eoe->tx_frame_number = 0xFF;
+ memset(&eoe->stats, 0, sizeof(struct net_device_stats));
+
+ eoe->rx_counter = 0;
+ eoe->tx_counter = 0;
+ eoe->rx_rate = 0;
+ eoe->tx_rate = 0;
+ eoe->rate_jiffies = 0;
+ eoe->rx_idle = 1;
+ eoe->tx_idle = 1;
+
+ /* device name eoe<MASTER>[as]<SLAVE>, because networking scripts don't
+ * like hyphens etc. in interface names. */
+ if (slave->effective_alias) {
+ snprintf(name, EC_DATAGRAM_NAME_SIZE,
+ "eoe%ua%u", slave->master->index, slave->effective_alias);
+ } else {
+ snprintf(name, EC_DATAGRAM_NAME_SIZE,
+ "eoe%us%u", slave->master->index, slave->ring_position);
+ }
+
+ snprintf(eoe->datagram.name, EC_DATAGRAM_NAME_SIZE, name);
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 17, 0)
+ eoe->dev = alloc_netdev(sizeof(ec_eoe_t *), name, NET_NAME_UNKNOWN,
+ ether_setup);
+#else
+ eoe->dev = alloc_netdev(sizeof(ec_eoe_t *), name, ether_setup);
+#endif
+ if (!eoe->dev) {
+ EC_SLAVE_ERR(slave, "Unable to allocate net_device %s"
+ " for EoE handler!\n", name);
+ ret = -ENODEV;
+ goto out_return;
+ }
+
+ // initialize net_device
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+ eoe->dev->netdev_ops = &ec_eoedev_ops;
+#else
+ eoe->dev->open = ec_eoedev_open;
+ eoe->dev->stop = ec_eoedev_stop;
+ eoe->dev->hard_start_xmit = ec_eoedev_tx;
+ eoe->dev->get_stats = ec_eoedev_stats;
+#endif
+
+ for (i = 0; i < ETH_ALEN; i++)
+ eoe->dev->dev_addr[i] = i | (i << 4);
+
+ // initialize private data
+ priv = netdev_priv(eoe->dev);
+ *priv = eoe;
+
+ // Usually setting the MTU appropriately makes the upper layers
+ // do the frame fragmenting. In some cases this doesn't work
+ // so the MTU is left on the Ethernet standard value and fragmenting
+ // is done "manually".
+#if 0
+ eoe->dev->mtu = slave->configured_rx_mailbox_size - ETH_HLEN - 10;
+#endif
+
+ // connect the net_device to the kernel
+ ret = register_netdev(eoe->dev);
+ if (ret) {
+ EC_SLAVE_ERR(slave, "Unable to register net_device:"
+ " error %i\n", ret);
+ goto out_free;
+ }
+
+ // make the last address octet unique
+ eoe->dev->dev_addr[ETH_ALEN - 1] = (uint8_t) eoe->dev->ifindex;
+ return 0;
+
+ out_free:
+ free_netdev(eoe->dev);
+ eoe->dev = NULL;
+ out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** EoE destructor.
+ *
+ * Unregisteres the net_device and frees allocated memory.
+ */
+void ec_eoe_clear(ec_eoe_t *eoe /**< EoE handler */)
+{
+ unregister_netdev(eoe->dev); // possibly calls close callback
+
+ // empty transmit queue
+ ec_eoe_flush(eoe);
+
+ if (eoe->tx_frame) {
+ dev_kfree_skb(eoe->tx_frame->skb);
+ kfree(eoe->tx_frame);
+ }
+
+ if (eoe->rx_skb)
+ dev_kfree_skb(eoe->rx_skb);
+
+ free_netdev(eoe->dev);
+
+ ec_datagram_clear(&eoe->datagram);
+}
+
+/*****************************************************************************/
+
+/** Empties the transmit queue.
+ */
+void ec_eoe_flush(ec_eoe_t *eoe /**< EoE handler */)
+{
+ ec_eoe_frame_t *frame, *next;
+
+ down(&eoe->tx_queue_sem);
+
+ list_for_each_entry_safe(frame, next, &eoe->tx_queue, queue) {
+ list_del(&frame->queue);
+ dev_kfree_skb(frame->skb);
+ kfree(frame);
+ }
+ eoe->tx_queued_frames = 0;
+
+ up(&eoe->tx_queue_sem);
+}
+
+/*****************************************************************************/
+
+/** Sends a frame or the next fragment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_eoe_send(ec_eoe_t *eoe /**< EoE handler */)
+{
+ size_t remaining_size, current_size, complete_offset;
+ unsigned int last_fragment;
+ uint8_t *data;
+#if EOE_DEBUG_LEVEL >= 3
+ unsigned int i;
+#endif
+
+ remaining_size = eoe->tx_frame->skb->len - eoe->tx_offset;
+
+ if (remaining_size <= eoe->slave->configured_tx_mailbox_size - 10) {
+ current_size = remaining_size;
+ last_fragment = 1;
+ } else {
+ current_size = ((eoe->slave->configured_tx_mailbox_size - 10) / 32) * 32;
+ last_fragment = 0;
+ }
+
+ if (eoe->tx_fragment_number) {
+ complete_offset = eoe->tx_offset / 32;
+ }
+ else {
+ // complete size in 32 bit blocks, rounded up.
+ complete_offset = remaining_size / 32 + 1;
+ }
+
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(slave, 0, "EoE %s TX sending fragment %u%s"
+ " with %u octets (%u). %u frames queued.\n",
+ eoe->dev->name, eoe->tx_fragment_number,
+ last_fragment ? "" : "+", current_size, complete_offset,
+ eoe->tx_queued_frames);
+#endif
+
+#if EOE_DEBUG_LEVEL >= 3
+ EC_SLAVE_DBG(master, 0, "");
+ for (i = 0; i < current_size; i++) {
+ printk("%02X ", eoe->tx_frame->skb->data[eoe->tx_offset + i]);
+ if ((i + 1) % 16 == 0) {
+ printk("\n");
+ EC_SLAVE_DBG(master, 0, "");
+ }
+ }
+ printk("\n");
+#endif
+
+ data = ec_slave_mbox_prepare_send(eoe->slave, &eoe->datagram,
+ 0x02, current_size + 4);
+ if (IS_ERR(data))
+ return PTR_ERR(data);
+
+ EC_WRITE_U8 (data, 0x00); // eoe fragment req.
+ EC_WRITE_U8 (data + 1, last_fragment);
+ EC_WRITE_U16(data + 2, ((eoe->tx_fragment_number & 0x3F) |
+ (complete_offset & 0x3F) << 6 |
+ (eoe->tx_frame_number & 0x0F) << 12));
+
+ memcpy(data + 4, eoe->tx_frame->skb->data + eoe->tx_offset, current_size);
+ eoe->queue_datagram = 1;
+
+ eoe->tx_offset += current_size;
+ eoe->tx_fragment_number++;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Runs the EoE state machine.
+ */
+void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (!eoe->opened)
+ return;
+
+ // if the datagram was not sent, or is not yet received, skip this cycle
+ if (eoe->queue_datagram || eoe->datagram.state == EC_DATAGRAM_SENT)
+ return;
+
+ // call state function
+ eoe->state(eoe);
+
+ // update statistics
+ if (jiffies - eoe->rate_jiffies > HZ) {
+ eoe->rx_rate = eoe->rx_counter;
+ eoe->tx_rate = eoe->tx_counter;
+ eoe->rx_counter = 0;
+ eoe->tx_counter = 0;
+ eoe->rate_jiffies = jiffies;
+ }
+
+ ec_datagram_output_stats(&eoe->datagram);
+}
+
+/*****************************************************************************/
+
+/** Queues the datagram, if necessary.
+ */
+void ec_eoe_queue(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->queue_datagram) {
+ ec_master_queue_datagram_ext(eoe->slave->master, &eoe->datagram);
+ eoe->queue_datagram = 0;
+ }
+}
+
+/*****************************************************************************/
+
+/** Returns the state of the device.
+ *
+ * \return 1 if the device is "up", 0 if it is "down"
+ */
+int ec_eoe_is_open(const ec_eoe_t *eoe /**< EoE handler */)
+{
+ return eoe->opened;
+}
+
+/*****************************************************************************/
+
+/** Returns the idle state.
+ *
+ * \retval 1 The device is idle.
+ * \retval 0 The device is busy.
+ */
+int ec_eoe_is_idle(const ec_eoe_t *eoe /**< EoE handler */)
+{
+ return eoe->rx_idle && eoe->tx_idle;
+}
+
+/******************************************************************************
+ * STATE PROCESSING FUNCTIONS
+ *****************************************************************************/
+
+/** State: RX_START.
+ *
+ * Starts a new receiving sequence by queueing a datagram that checks the
+ * slave's mailbox for a new EoE datagram.
+ *
+ * \todo Use both devices.
+ */
+void ec_eoe_state_rx_start(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->slave->error_flag ||
+ !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
+ eoe->rx_idle = 1;
+ eoe->tx_idle = 1;
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(eoe->slave, &eoe->datagram);
+ eoe->queue_datagram = 1;
+ eoe->state = ec_eoe_state_rx_check;
+}
+
+/*****************************************************************************/
+
+/** State: RX_CHECK.
+ *
+ * Processes the checking datagram sent in RX_START and issues a receive
+ * datagram, if new data is available.
+ */
+void ec_eoe_state_rx_check(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
+ eoe->stats.rx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Failed to receive mbox"
+ " check datagram for %s.\n", eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ if (!ec_slave_mbox_check(&eoe->datagram)) {
+ eoe->rx_idle = 1;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ eoe->rx_idle = 0;
+ ec_slave_mbox_prepare_fetch(eoe->slave, &eoe->datagram);
+ eoe->queue_datagram = 1;
+ eoe->state = ec_eoe_state_rx_fetch;
+}
+
+/*****************************************************************************/
+
+/** State: RX_FETCH.
+ *
+ * Checks if the requested data of RX_CHECK was received and processes the EoE
+ * datagram.
+ */
+void ec_eoe_state_rx_fetch(ec_eoe_t *eoe /**< EoE handler */)
+{
+ size_t rec_size, data_size;
+ uint8_t *data, frame_type, last_fragment, time_appended, mbox_prot;
+ uint8_t fragment_offset, fragment_number;
+#if EOE_DEBUG_LEVEL >= 2
+ uint8_t frame_number;
+#endif
+ off_t offset;
+#if EOE_DEBUG_LEVEL >= 3
+ unsigned int i;
+#endif
+
+ if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
+ eoe->stats.rx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Failed to receive mbox"
+ " fetch datagram for %s.\n", eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ data = ec_slave_mbox_fetch(eoe->slave, &eoe->datagram,
+ &mbox_prot, &rec_size);
+ if (IS_ERR(data)) {
+ eoe->stats.rx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Invalid mailbox response for %s.\n",
+ eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ if (mbox_prot != 0x02) { // EoE FIXME mailbox handler necessary
+ eoe->stats.rx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Other mailbox protocol response for %s.\n",
+ eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ frame_type = EC_READ_U16(data) & 0x000F;
+
+ if (frame_type != 0x00) {
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "%s: Other frame received."
+ " Dropping.\n", eoe->dev->name);
+#endif
+ eoe->stats.rx_dropped++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ // EoE Fragment Request received
+
+ last_fragment = (EC_READ_U16(data) >> 8) & 0x0001;
+ time_appended = (EC_READ_U16(data) >> 9) & 0x0001;
+ fragment_number = EC_READ_U16(data + 2) & 0x003F;
+ fragment_offset = (EC_READ_U16(data + 2) >> 6) & 0x003F;
+#if EOE_DEBUG_LEVEL >= 2
+ frame_number = (EC_READ_U16(data + 2) >> 12) & 0x000F;
+#endif
+
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX fragment %u%s, offset %u,"
+ " frame %u%s, %u octets\n", eoe->dev->name, fragment_number,
+ last_fragment ? "" : "+", fragment_offset, frame_number,
+ time_appended ? ", + timestamp" : "",
+ time_appended ? rec_size - 8 : rec_size - 4);
+#endif
+
+#if EOE_DEBUG_LEVEL >= 3
+ EC_SLAVE_DBG(eoe->slave, 0, "");
+ for (i = 0; i < rec_size - 4; i++) {
+ printk("%02X ", data[i + 4]);
+ if ((i + 1) % 16 == 0) {
+ printk("\n");
+ EC_SLAVE_DBG(eoe->slave, 0, "");
+ }
+ }
+ printk("\n");
+#endif
+
+ data_size = time_appended ? rec_size - 8 : rec_size - 4;
+
+ if (!fragment_number) {
+ if (eoe->rx_skb) {
+ EC_SLAVE_WARN(eoe->slave, "EoE RX freeing old socket buffer.\n");
+ dev_kfree_skb(eoe->rx_skb);
+ }
+
+ // new socket buffer
+ if (!(eoe->rx_skb = dev_alloc_skb(fragment_offset * 32))) {
+ if (printk_ratelimit())
+ EC_SLAVE_WARN(eoe->slave, "EoE RX low on mem,"
+ " frame dropped.\n");
+ eoe->stats.rx_dropped++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ eoe->rx_skb_offset = 0;
+ eoe->rx_skb_size = fragment_offset * 32;
+ eoe->rx_expected_fragment = 0;
+ }
+ else {
+ if (!eoe->rx_skb) {
+ eoe->stats.rx_dropped++;
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+
+ offset = fragment_offset * 32;
+ if (offset != eoe->rx_skb_offset ||
+ offset + data_size > eoe->rx_skb_size ||
+ fragment_number != eoe->rx_expected_fragment) {
+ dev_kfree_skb(eoe->rx_skb);
+ eoe->rx_skb = NULL;
+ eoe->stats.rx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Fragmenting error at %s.\n",
+ eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_tx_start;
+ return;
+ }
+ }
+
+ // copy fragment into socket buffer
+ memcpy(skb_put(eoe->rx_skb, data_size), data + 4, data_size);
+ eoe->rx_skb_offset += data_size;
+
+ if (last_fragment) {
+ // update statistics
+ eoe->stats.rx_packets++;
+ eoe->stats.rx_bytes += eoe->rx_skb->len;
+ eoe->rx_counter += eoe->rx_skb->len;
+
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX frame completed"
+ " with %u octets.\n", eoe->dev->name, eoe->rx_skb->len);
+#endif
+
+ // pass socket buffer to network stack
+ eoe->rx_skb->dev = eoe->dev;
+ eoe->rx_skb->protocol = eth_type_trans(eoe->rx_skb, eoe->dev);
+ eoe->rx_skb->ip_summed = CHECKSUM_UNNECESSARY;
+ if (netif_rx(eoe->rx_skb)) {
+ EC_SLAVE_WARN(eoe->slave, "EoE RX netif_rx failed.\n");
+ }
+ eoe->rx_skb = NULL;
+
+ eoe->state = ec_eoe_state_tx_start;
+ }
+ else {
+ eoe->rx_expected_fragment++;
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "EoE %s RX expecting fragment %u\n",
+ eoe->dev->name, eoe->rx_expected_fragment);
+#endif
+ eoe->state = ec_eoe_state_rx_start;
+ }
+}
+
+/*****************************************************************************/
+
+/** State: TX START.
+ *
+ * Starts a new transmit sequence. If no data is available, a new receive
+ * sequence is started instead.
+ *
+ * \todo Use both devices.
+ */
+void ec_eoe_state_tx_start(ec_eoe_t *eoe /**< EoE handler */)
+{
+#if EOE_DEBUG_LEVEL >= 2
+ unsigned int wakeup = 0;
+#endif
+
+ if (eoe->slave->error_flag ||
+ !eoe->slave->master->devices[EC_DEVICE_MAIN].link_state) {
+ eoe->rx_idle = 1;
+ eoe->tx_idle = 1;
+ return;
+ }
+
+ down(&eoe->tx_queue_sem);
+
+ if (!eoe->tx_queued_frames || list_empty(&eoe->tx_queue)) {
+ up(&eoe->tx_queue_sem);
+ eoe->tx_idle = 1;
+ // no data available.
+ // start a new receive immediately.
+ ec_eoe_state_rx_start(eoe);
+ return;
+ }
+
+ // take the first frame out of the queue
+ eoe->tx_frame = list_entry(eoe->tx_queue.next, ec_eoe_frame_t, queue);
+ list_del(&eoe->tx_frame->queue);
+ if (!eoe->tx_queue_active &&
+ eoe->tx_queued_frames == eoe->tx_queue_size / 2) {
+ netif_wake_queue(eoe->dev);
+ eoe->tx_queue_active = 1;
+#if EOE_DEBUG_LEVEL >= 2
+ wakeup = 1;
+#endif
+ }
+
+ eoe->tx_queued_frames--;
+ up(&eoe->tx_queue_sem);
+
+ eoe->tx_idle = 0;
+
+ eoe->tx_frame_number++;
+ eoe->tx_frame_number %= 16;
+ eoe->tx_fragment_number = 0;
+ eoe->tx_offset = 0;
+
+ if (ec_eoe_send(eoe)) {
+ dev_kfree_skb(eoe->tx_frame->skb);
+ kfree(eoe->tx_frame);
+ eoe->tx_frame = NULL;
+ eoe->stats.tx_errors++;
+ eoe->state = ec_eoe_state_rx_start;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Send error at %s.\n", eoe->dev->name);
+#endif
+ return;
+ }
+
+#if EOE_DEBUG_LEVEL >= 2
+ if (wakeup)
+ EC_SLAVE_DBG(eoe->slave, 0, "EoE %s waking up TX queue...\n",
+ eoe->dev->name);
+#endif
+
+ eoe->tries = EC_EOE_TRIES;
+ eoe->state = ec_eoe_state_tx_sent;
+}
+
+/*****************************************************************************/
+
+/** State: TX SENT.
+ *
+ * Checks is the previous transmit datagram succeded and sends the next
+ * fragment, if necessary.
+ */
+void ec_eoe_state_tx_sent(ec_eoe_t *eoe /**< EoE handler */)
+{
+ if (eoe->datagram.state != EC_DATAGRAM_RECEIVED) {
+ if (eoe->tries) {
+ eoe->tries--; // try again
+ eoe->queue_datagram = 1;
+ } else {
+ eoe->stats.tx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Failed to receive send"
+ " datagram for %s after %u tries.\n",
+ eoe->dev->name, EC_EOE_TRIES);
+#endif
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ return;
+ }
+
+ if (eoe->datagram.working_counter != 1) {
+ if (eoe->tries) {
+ eoe->tries--; // try again
+ eoe->queue_datagram = 1;
+ } else {
+ eoe->stats.tx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "No sending response"
+ " for %s after %u tries.\n",
+ eoe->dev->name, EC_EOE_TRIES);
+#endif
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ return;
+ }
+
+ // frame completely sent
+ if (eoe->tx_offset >= eoe->tx_frame->skb->len) {
+ eoe->stats.tx_packets++;
+ eoe->stats.tx_bytes += eoe->tx_frame->skb->len;
+ eoe->tx_counter += eoe->tx_frame->skb->len;
+ dev_kfree_skb(eoe->tx_frame->skb);
+ kfree(eoe->tx_frame);
+ eoe->tx_frame = NULL;
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ else { // send next fragment
+ if (ec_eoe_send(eoe)) {
+ dev_kfree_skb(eoe->tx_frame->skb);
+ kfree(eoe->tx_frame);
+ eoe->tx_frame = NULL;
+ eoe->stats.tx_errors++;
+#if EOE_DEBUG_LEVEL >= 1
+ EC_SLAVE_WARN(eoe->slave, "Send error at %s.\n", eoe->dev->name);
+#endif
+ eoe->state = ec_eoe_state_rx_start;
+ }
+ }
+}
+
+/******************************************************************************
+ * NET_DEVICE functions
+ *****************************************************************************/
+
+/** Opens the virtual network device.
+ *
+ * \return Always zero (success).
+ */
+int ec_eoedev_open(struct net_device *dev /**< EoE net_device */)
+{
+ ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
+ ec_eoe_flush(eoe);
+ eoe->opened = 1;
+ eoe->rx_idle = 0;
+ eoe->tx_idle = 0;
+ netif_start_queue(dev);
+ eoe->tx_queue_active = 1;
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "%s opened.\n", dev->name);
+#endif
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Stops the virtual network device.
+ *
+ * \return Always zero (success).
+ */
+int ec_eoedev_stop(struct net_device *dev /**< EoE net_device */)
+{
+ ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
+ netif_stop_queue(dev);
+ eoe->rx_idle = 1;
+ eoe->tx_idle = 1;
+ eoe->tx_queue_active = 0;
+ eoe->opened = 0;
+ ec_eoe_flush(eoe);
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "%s stopped.\n", dev->name);
+#endif
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_PREOP);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Transmits data via the virtual network device.
+ *
+ * \return Zero on success, non-zero on failure.
+ */
+int ec_eoedev_tx(struct sk_buff *skb, /**< transmit socket buffer */
+ struct net_device *dev /**< EoE net_device */
+ )
+{
+ ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
+ ec_eoe_frame_t *frame;
+
+#if 0
+ if (skb->len > eoe->slave->configured_tx_mailbox_size - 10) {
+ EC_SLAVE_WARN(eoe->slave, "EoE TX frame (%u octets)"
+ " exceeds MTU. dropping.\n", skb->len);
+ dev_kfree_skb(skb);
+ eoe->stats.tx_dropped++;
+ return 0;
+ }
+#endif
+
+ if (!(frame =
+ (ec_eoe_frame_t *) kmalloc(sizeof(ec_eoe_frame_t), GFP_ATOMIC))) {
+ if (printk_ratelimit())
+ EC_SLAVE_WARN(eoe->slave, "EoE TX: low on mem. frame dropped.\n");
+ return 1;
+ }
+
+ frame->skb = skb;
+
+ down(&eoe->tx_queue_sem);
+ list_add_tail(&frame->queue, &eoe->tx_queue);
+ eoe->tx_queued_frames++;
+ if (eoe->tx_queued_frames == eoe->tx_queue_size) {
+ netif_stop_queue(dev);
+ eoe->tx_queue_active = 0;
+ }
+ up(&eoe->tx_queue_sem);
+
+#if EOE_DEBUG_LEVEL >= 2
+ EC_SLAVE_DBG(eoe->slave, 0, "EoE %s TX queued frame"
+ " with %u octets (%u frames queued).\n",
+ eoe->dev->name, skb->len, eoe->tx_queued_frames);
+ if (!eoe->tx_queue_active)
+ EC_SLAVE_WARN(eoe->slave, "EoE TX queue is now full.\n");
+#endif
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets statistics about the virtual network device.
+ *
+ * \return Statistics.
+ */
+struct net_device_stats *ec_eoedev_stats(
+ struct net_device *dev /**< EoE net_device */
+ )
+{
+ ec_eoe_t *eoe = *((ec_eoe_t **) netdev_priv(dev));
+ return &eoe->stats;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/ethernet.h b/net/ethercat/master/ethernet.h
new file mode 100644
index 000000000000..b0b3860ce1d5
--- /dev/null
+++ b/net/ethercat/master/ethernet.h
@@ -0,0 +1,125 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 (EoE)
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_ETHERNET_H__
+#define __EC_ETHERNET_H__
+
+#include <linux/list.h>
+#include <linux/netdevice.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+#include <linux/semaphore.h>
+#else
+#include <asm/semaphore.h>
+#endif
+
+#include "globals.h"
+#include "slave.h"
+#include "datagram.h"
+
+/*****************************************************************************/
+
+/**
+ Queued frame structure.
+*/
+
+typedef struct
+{
+ struct list_head queue; /**< list item */
+ struct sk_buff *skb; /**< socket buffer */
+}
+ec_eoe_frame_t;
+
+/*****************************************************************************/
+
+typedef struct ec_eoe ec_eoe_t; /**< \see ec_eoe */
+
+/**
+ Ethernet over EtherCAT (EoE) handler.
+ The master creates one of these objects for each slave that supports the
+ EoE protocol.
+*/
+
+struct ec_eoe
+{
+ struct list_head list; /**< list item */
+ ec_slave_t *slave; /**< pointer to the corresponding slave */
+ ec_datagram_t datagram; /**< datagram */
+ unsigned int queue_datagram; /**< the datagram is ready for queuing */
+ void (*state)(ec_eoe_t *); /**< state function for the state machine */
+ struct net_device *dev; /**< net_device for virtual ethernet device */
+ struct net_device_stats stats; /**< device statistics */
+ unsigned int opened; /**< net_device is opened */
+ unsigned long rate_jiffies; /**< time of last rate output */
+
+ struct sk_buff *rx_skb; /**< current rx socket buffer */
+ off_t rx_skb_offset; /**< current write pointer in the socket buffer */
+ size_t rx_skb_size; /**< size of the allocated socket buffer memory */
+ uint8_t rx_expected_fragment; /**< next expected fragment number */
+ uint32_t rx_counter; /**< octets received during last second */
+ uint32_t rx_rate; /**< receive rate (bps) */
+ unsigned int rx_idle; /**< Idle flag. */
+
+ struct list_head tx_queue; /**< queue for frames to send */
+ unsigned int tx_queue_size; /**< Transmit queue size. */
+ unsigned int tx_queue_active; /**< kernel netif queue started */
+ unsigned int tx_queued_frames; /**< number of frames in the queue */
+ struct semaphore tx_queue_sem; /**< Semaphore for the send queue. */
+ ec_eoe_frame_t *tx_frame; /**< current TX frame */
+ uint8_t tx_frame_number; /**< number of the transmitted frame */
+ uint8_t tx_fragment_number; /**< number of the fragment */
+ size_t tx_offset; /**< number of octets sent */
+ uint32_t tx_counter; /**< octets transmitted during last second */
+ uint32_t tx_rate; /**< transmit rate (bps) */
+ unsigned int tx_idle; /**< Idle flag. */
+
+ unsigned int tries; /**< Tries. */
+};
+
+/*****************************************************************************/
+
+int ec_eoe_init(ec_eoe_t *, ec_slave_t *);
+void ec_eoe_clear(ec_eoe_t *);
+void ec_eoe_run(ec_eoe_t *);
+void ec_eoe_queue(ec_eoe_t *);
+int ec_eoe_is_open(const ec_eoe_t *);
+int ec_eoe_is_idle(const ec_eoe_t *);
+
+/*****************************************************************************/
+
+#endif
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fmmu_config.c b/net/ethercat/master/fmmu_config.c
new file mode 100644
index 000000000000..957100f2ef3c
--- /dev/null
+++ b/net/ethercat/master/fmmu_config.c
@@ -0,0 +1,99 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 FMMU configuration methods.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "slave_config.h"
+#include "master.h"
+
+#include "fmmu_config.h"
+
+/*****************************************************************************/
+
+/** FMMU configuration constructor.
+ *
+ * Inits an FMMU configuration, sets the logical start address and adds the
+ * process data size for the mapped PDOs of the given direction to the domain
+ * data size.
+ */
+void ec_fmmu_config_init(
+ ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */
+ ec_slave_config_t *sc, /**< EtherCAT slave configuration. */
+ ec_domain_t *domain, /**< EtherCAT domain. */
+ uint8_t sync_index, /**< Sync manager index to use. */
+ ec_direction_t dir /**< PDO direction. */
+ )
+{
+ INIT_LIST_HEAD(&fmmu->list);
+ fmmu->sc = sc;
+ fmmu->sync_index = sync_index;
+ fmmu->dir = dir;
+
+ fmmu->logical_start_address = domain->data_size;
+ fmmu->data_size = ec_pdo_list_total_size(
+ &sc->sync_configs[sync_index].pdos);
+
+ ec_domain_add_fmmu_config(domain, fmmu);
+}
+
+/*****************************************************************************/
+
+/** Initializes an FMMU configuration page.
+ *
+ * The referenced memory (\a data) must be at least EC_FMMU_PAGE_SIZE bytes.
+ */
+void ec_fmmu_config_page(
+ const ec_fmmu_config_t *fmmu, /**< EtherCAT FMMU configuration. */
+ const ec_sync_t *sync, /**< Sync manager. */
+ uint8_t *data /**> Configuration page memory. */
+ )
+{
+ EC_CONFIG_DBG(fmmu->sc, 1, "FMMU: LogAddr 0x%08X, Size %3u,"
+ " PhysAddr 0x%04X, SM%u, Dir %s\n",
+ fmmu->logical_start_address, fmmu->data_size,
+ sync->physical_start_address, fmmu->sync_index,
+ fmmu->dir == EC_DIR_INPUT ? "in" : "out");
+
+ EC_WRITE_U32(data, fmmu->logical_start_address);
+ EC_WRITE_U16(data + 4, fmmu->data_size); // size of fmmu
+ EC_WRITE_U8 (data + 6, 0x00); // logical start bit
+ EC_WRITE_U8 (data + 7, 0x07); // logical end bit
+ EC_WRITE_U16(data + 8, sync->physical_start_address);
+ EC_WRITE_U8 (data + 10, 0x00); // physical start bit
+ EC_WRITE_U8 (data + 11, fmmu->dir == EC_DIR_INPUT ? 0x01 : 0x02);
+ EC_WRITE_U16(data + 12, 0x0001); // enable
+ EC_WRITE_U16(data + 14, 0x0000); // reserved
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fmmu_config.h b/net/ethercat/master/fmmu_config.h
new file mode 100644
index 000000000000..4803f3fc9175
--- /dev/null
+++ b/net/ethercat/master/fmmu_config.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 FMMU configuration structure.
+ */
+
+/*****************************************************************************/
+
+#ifndef __EC_FMMU_CONFIG_H__
+#define __EC_FMMU_CONFIG_H__
+
+#include "globals.h"
+#include "sync.h"
+
+/*****************************************************************************/
+
+/** FMMU configuration.
+ */
+typedef struct {
+ struct list_head list; /**< List node used by domain. */
+ const ec_slave_config_t *sc; /**< EtherCAT slave config. */
+ const ec_domain_t *domain; /**< Domain. */
+ uint8_t sync_index; /**< Index of sync manager to use. */
+ ec_direction_t dir; /**< FMMU direction. */
+ uint32_t logical_start_address; /**< Logical start address. */
+ unsigned int data_size; /**< Covered PDO size. */
+} ec_fmmu_config_t;
+
+/*****************************************************************************/
+
+void ec_fmmu_config_init(ec_fmmu_config_t *, ec_slave_config_t *,
+ ec_domain_t *, uint8_t, ec_direction_t);
+
+void ec_fmmu_config_page(const ec_fmmu_config_t *, const ec_sync_t *,
+ uint8_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/foe.h b/net/ethercat/master/foe.h
new file mode 100644
index 000000000000..44090b0b7435
--- /dev/null
+++ b/net/ethercat/master/foe.h
@@ -0,0 +1,62 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ * FoE defines.
+ */
+
+#ifndef __FOE_H__
+#define __FOE_H__
+
+/*****************************************************************************/
+
+/** FoE error enumeration type.
+ */
+typedef enum {
+ FOE_BUSY = 0, /**< Busy. */
+ FOE_READY = 1, /**< Ready. */
+ FOE_IDLE = 2, /**< Idle. */
+ FOE_WC_ERROR = 3, /**< Working counter error. */
+ FOE_RECEIVE_ERROR = 4, /**< Receive error. */
+ FOE_PROT_ERROR = 5, /**< Protocol error. */
+ FOE_NODATA_ERROR = 6, /**< No data error. */
+ FOE_PACKETNO_ERROR = 7, /**< Packet number error. */
+ FOE_OPCODE_ERROR = 8, /**< OpCode error. */
+ FOE_TIMEOUT_ERROR = 9, /**< Timeout error. */
+ FOE_SEND_RX_DATA_ERROR = 10, /**< Error sending received data. */
+ FOE_RX_DATA_ACK_ERROR = 11, /**< Error acknowledging received data. */
+ FOE_ACK_ERROR = 12, /**< Acknowledge error. */
+ FOE_MBOX_FETCH_ERROR = 13, /**< Error fetching data from mailbox. */
+ FOE_READ_NODATA_ERROR = 14, /**< No data while reading. */
+ FOE_MBOX_PROT_ERROR = 15, /**< Mailbox protocol error. */
+} ec_foe_error_t;
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/foe_request.c b/net/ethercat/master/foe_request.c
new file mode 100644
index 000000000000..66a65e8f9c17
--- /dev/null
+++ b/net/ethercat/master/foe_request.c
@@ -0,0 +1,238 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2008 Olav Zarges, imc Messsysteme GmbH
+ * Copyright (C) 2020 Florian Pose, 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
+ * File-over-EtherCAT request functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "foe_request.h"
+#include "foe.h"
+
+/*****************************************************************************/
+
+/** Default timeout in ms to wait for FoE transfer responses.
+ */
+#define EC_FOE_REQUEST_RESPONSE_TIMEOUT 3000
+
+/*****************************************************************************/
+
+void ec_foe_request_clear_data(ec_foe_request_t *);
+
+/*****************************************************************************/
+
+/** FoE request constructor.
+ */
+void ec_foe_request_init(
+ ec_foe_request_t *req, /**< FoE request. */
+ uint8_t* file_name /** filename */)
+{
+ INIT_LIST_HEAD(&req->list);
+ req->buffer = NULL;
+ req->file_name = file_name;
+ req->buffer_size = 0;
+ req->data_size = 0;
+ req->dir = EC_DIR_INVALID;
+ req->issue_timeout = 0; // no timeout
+ req->response_timeout = EC_FOE_REQUEST_RESPONSE_TIMEOUT;
+ req->state = EC_INT_REQUEST_INIT;
+ req->result = FOE_BUSY;
+ req->error_code = 0x00000000;
+}
+
+/*****************************************************************************/
+
+/** FoE request destructor.
+ */
+void ec_foe_request_clear(
+ ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ ec_foe_request_clear_data(req);
+}
+
+/*****************************************************************************/
+
+/** FoE request destructor.
+ */
+void ec_foe_request_clear_data(
+ ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ if (req->buffer) {
+ vfree(req->buffer);
+ req->buffer = NULL;
+ }
+
+ req->buffer_size = 0;
+ req->data_size = 0;
+}
+
+/*****************************************************************************/
+
+/** Pre-allocates the data memory.
+ *
+ * If the internal \a buffer_size is already bigger than \a size, nothing is
+ * done.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_request_alloc(
+ ec_foe_request_t *req, /**< FoE request. */
+ size_t size /**< Data size to allocate. */
+ )
+{
+ if (size <= req->buffer_size) {
+ return 0;
+ }
+
+ ec_foe_request_clear_data(req);
+
+ if (!(req->buffer = (uint8_t *) vmalloc(size))) {
+ EC_ERR("Failed to allocate %zu bytes of FoE memory.\n", size);
+ return -ENOMEM;
+ }
+
+ req->buffer_size = size;
+ req->data_size = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Copies FoE data from an external source.
+ *
+ * If the \a buffer_size is to small, new memory is allocated.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_request_copy_data(
+ ec_foe_request_t *req, /**< FoE request. */
+ const uint8_t *source, /**< Source data. */
+ size_t size /**< Number of bytes in \a source. */
+ )
+{
+ int ret;
+
+ ret = ec_foe_request_alloc(req, size);
+ if (ret) {
+ return ret;
+ }
+
+ memcpy(req->buffer, source, size);
+ req->data_size = size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Checks, if the timeout was exceeded.
+ *
+ * \return non-zero if the timeout was exceeded, else zero.
+ */
+int ec_foe_request_timed_out(
+ const ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ return req->issue_timeout
+ && jiffies - req->jiffies_start > HZ * req->issue_timeout / 1000;
+}
+
+/*****************************************************************************/
+
+/** Set the request timeout.
+ */
+void ec_foe_request_timeout(
+ ec_foe_request_t *req, /**< FoE request. */
+ uint32_t timeout /**< Timeout in ms. */
+ )
+{
+ req->issue_timeout = timeout;
+}
+
+/*****************************************************************************/
+
+/** Returns a pointer to the request's data.
+ *
+ * \return Data pointer.
+ */
+uint8_t *ec_foe_request_data(
+ ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ return req->buffer;
+}
+
+/*****************************************************************************/
+
+/** Returns the data size.
+ *
+ * \return Data size.
+ */
+size_t ec_foe_request_data_size(
+ const ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ return req->data_size;
+}
+
+/*****************************************************************************/
+
+/** Prepares a read request (slave to master).
+ */
+void ec_foe_request_read(
+ ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ req->dir = EC_DIR_INPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->result = FOE_BUSY;
+ req->jiffies_start = jiffies;
+}
+
+/*****************************************************************************/
+
+/** Prepares a write request (master to slave).
+ */
+void ec_foe_request_write(
+ ec_foe_request_t *req /**< FoE request. */
+ )
+{
+ req->dir = EC_DIR_OUTPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->result = FOE_BUSY;
+ req->jiffies_start = jiffies;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/foe_request.h b/net/ethercat/master/foe_request.h
new file mode 100644
index 000000000000..25b9e63821ff
--- /dev/null
+++ b/net/ethercat/master/foe_request.h
@@ -0,0 +1,86 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2008 Olav Zarges, imc Messsysteme GmbH
+ *
+ * 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 FoE request structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FOE_REQUEST_H__
+#define __EC_FOE_REQUEST_H__
+
+#include <linux/list.h>
+
+#include "../include/ecrt.h"
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** FoE request.
+ */
+typedef struct {
+ struct list_head list; /**< List item. */
+ uint8_t *buffer; /**< Pointer to FoE data. */
+ size_t buffer_size; /**< Size of FoE data memory. */
+ size_t data_size; /**< Size of FoE data. */
+
+ uint32_t issue_timeout; /**< Maximum time in ms, the processing of the
+ request may take. */
+ uint32_t response_timeout; /**< Maximum time in ms, the transfer is
+ retried, if the slave does not respond. */
+ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means downloading to
+ the slave, EC_DIR_INPUT means uploading from the
+ slave. */
+ ec_internal_request_state_t state; /**< FoE request state. */
+ unsigned long jiffies_start; /**< Jiffies, when the request was issued. */
+ unsigned long jiffies_sent; /**< Jiffies, when the upload/download
+ request was sent. */
+ uint8_t *file_name; /**< Pointer to the filename. */
+ uint32_t result; /**< FoE request abort code. Zero on success. */
+ uint32_t error_code; /**< Error code from an FoE Error Request. */
+} ec_foe_request_t;
+
+/*****************************************************************************/
+
+void ec_foe_request_init(ec_foe_request_t *, uint8_t *file_name);
+void ec_foe_request_clear(ec_foe_request_t *);
+
+int ec_foe_request_alloc(ec_foe_request_t *, size_t);
+int ec_foe_request_copy_data(ec_foe_request_t *, const uint8_t *, size_t);
+int ec_foe_request_timed_out(const ec_foe_request_t *);
+
+void ec_foe_request_write(ec_foe_request_t *);
+void ec_foe_request_read(ec_foe_request_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_change.c b/net/ethercat/master/fsm_change.c
new file mode 100644
index 000000000000..584a20270d55
--- /dev/null
+++ b/net/ethercat/master/fsm_change.c
@@ -0,0 +1,577 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 state change FSM.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "fsm_change.h"
+
+/*****************************************************************************/
+
+/** Timeout while waiting for AL state change [s].
+ */
+#define EC_AL_STATE_CHANGE_TIMEOUT 5
+
+/*****************************************************************************/
+
+void ec_fsm_change_state_start(ec_fsm_change_t *);
+void ec_fsm_change_state_check(ec_fsm_change_t *);
+void ec_fsm_change_state_status(ec_fsm_change_t *);
+void ec_fsm_change_state_start_code(ec_fsm_change_t *);
+void ec_fsm_change_state_code(ec_fsm_change_t *);
+void ec_fsm_change_state_ack(ec_fsm_change_t *);
+void ec_fsm_change_state_check_ack(ec_fsm_change_t *);
+void ec_fsm_change_state_end(ec_fsm_change_t *);
+void ec_fsm_change_state_error(ec_fsm_change_t *);
+
+/*****************************************************************************/
+
+/**
+ Constructor.
+*/
+
+void ec_fsm_change_init(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = datagram;
+ fsm->spontaneous_change = 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Destructor.
+*/
+
+void ec_fsm_change_clear(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Starts the change state machine.
+*/
+
+void ec_fsm_change_start(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< EtherCAT slave */
+ ec_slave_state_t state /**< requested state */
+ )
+{
+ fsm->mode = EC_FSM_CHANGE_MODE_FULL;
+ fsm->slave = slave;
+ fsm->requested_state = state;
+ fsm->state = ec_fsm_change_state_start;
+}
+
+/*****************************************************************************/
+
+/**
+ Starts the change state machine to only acknowlegde a slave's state.
+*/
+
+void ec_fsm_change_ack(ec_fsm_change_t *fsm, /**< finite state machine */
+ ec_slave_t *slave /**< EtherCAT slave */
+ )
+{
+ fsm->mode = EC_FSM_CHANGE_MODE_ACK_ONLY;
+ fsm->slave = slave;
+ fsm->requested_state = EC_SLAVE_STATE_UNKNOWN;
+ fsm->state = ec_fsm_change_state_start_code;
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the current state of the state machine.
+ \return false, if the state machine has terminated
+*/
+
+int ec_fsm_change_exec(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+ fsm->state(fsm);
+
+ return fsm->state != ec_fsm_change_state_end
+ && fsm->state != ec_fsm_change_state_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Returns, if the state machine terminated with success.
+ \return non-zero if successful.
+*/
+
+int ec_fsm_change_success(ec_fsm_change_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_change_state_end;
+}
+
+/******************************************************************************
+ * state change state machine
+ *****************************************************************************/
+
+/**
+ Change state: START.
+*/
+
+void ec_fsm_change_state_start(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ fsm->take_time = 1;
+ fsm->old_state = fsm->slave->current_state;
+
+ // write new state to slave
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, fsm->requested_state);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_change_state_check;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: CHECK.
+*/
+
+void ec_fsm_change_state_check(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive state datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ if (datagram->working_counter == 0) {
+ if (datagram->jiffies_received - fsm->jiffies_start >= 3 * HZ) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, state_str, 0);
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str);
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ // repeat writing new state to slave
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, fsm->requested_state);
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+
+ if (unlikely(datagram->working_counter > 1)) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, state_str, 0);
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set state %s: ", state_str);
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ fsm->take_time = 1;
+
+ // read AL status from slave
+ ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->spontaneous_change = 0;
+ fsm->state = ec_fsm_change_state_status;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: STATUS.
+*/
+
+void ec_fsm_change_state_status(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive state checking datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ char req_state[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, req_state, 0);
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to check state %s: ", req_state);
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ slave->current_state = EC_READ_U8(datagram->data);
+
+ if (slave->current_state == fsm->requested_state) {
+ // state has been set successfully
+ fsm->state = ec_fsm_change_state_end;
+ return;
+ }
+
+ if (slave->current_state != fsm->old_state) { // state changed
+ char req_state[EC_STATE_STRING_SIZE], cur_state[EC_STATE_STRING_SIZE];
+
+ ec_state_string(slave->current_state, cur_state, 0);
+
+ if ((slave->current_state & 0x0F) != (fsm->old_state & 0x0F)) {
+ // Slave spontaneously changed its state just before the new state
+ // was written. Accept current state as old state and wait for
+ // state change
+ fsm->spontaneous_change = 1;
+ fsm->old_state = slave->current_state;
+ EC_SLAVE_WARN(slave, "Changed to %s in the meantime.\n",
+ cur_state);
+ goto check_again;
+ }
+
+ // state change error
+
+ slave->error_flag = 1;
+ ec_state_string(fsm->requested_state, req_state, 0);
+
+ EC_SLAVE_ERR(slave, "Failed to set %s state, slave refused state"
+ " change (%s).\n", req_state, cur_state);
+
+ ec_fsm_change_state_start_code(fsm);
+ return;
+ }
+
+ // still old state
+
+ if (datagram->jiffies_received - fsm->jiffies_start >=
+ EC_AL_STATE_CHANGE_TIMEOUT * HZ) {
+ // timeout while checking
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(fsm->requested_state, state_str, 0);
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Timeout while setting state %s.\n", state_str);
+ return;
+ }
+
+ check_again:
+ // no timeout yet. check again
+ ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+}
+
+/*****************************************************************************/
+
+/** Enter reading AL status code.
+ */
+void ec_fsm_change_state_start_code(
+ ec_fsm_change_t *fsm /**< finite state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ // fetch AL status error code
+ ec_datagram_fprd(datagram, slave->station_address, 0x0134, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_change_state_code;
+}
+
+/*****************************************************************************/
+
+/**
+ Application layer status messages.
+*/
+
+const ec_code_msg_t al_status_messages[] = {
+ {0x0000, "No error"},
+ {0x0001, "Unspecified error"},
+ {0x0002, "No Memory"},
+ {0x0011, "Invalid requested state change"},
+ {0x0012, "Unknown requested state"},
+ {0x0013, "Bootstrap not supported"},
+ {0x0014, "No valid firmware"},
+ {0x0015, "Invalid mailbox configuration"},
+ {0x0016, "Invalid mailbox configuration"},
+ {0x0017, "Invalid sync manager configuration"},
+ {0x0018, "No valid inputs available"},
+ {0x0019, "No valid outputs"},
+ {0x001A, "Synchronization error"},
+ {0x001B, "Sync manager watchdog"},
+ {0x001C, "Invalid sync manager types"},
+ {0x001D, "Invalid output configuration"},
+ {0x001E, "Invalid input configuration"},
+ {0x001F, "Invalid watchdog configuration"},
+ {0x0020, "Slave needs cold start"},
+ {0x0021, "Slave needs INIT"},
+ {0x0022, "Slave needs PREOP"},
+ {0x0023, "Slave needs SAFEOP"},
+ {0x0024, "Invalid Input Mapping"},
+ {0x0025, "Invalid Output Mapping"},
+ {0x0026, "Inconsistent Settings"},
+ {0x0027, "Freerun not supported"},
+ {0x0028, "Synchronization not supported"},
+ {0x0029, "Freerun needs 3 Buffer Mode"},
+ {0x002A, "Background Watchdog"},
+ {0x002B, "No Valid Inputs and Outputs"},
+ {0x002C, "Fatal Sync Error"},
+ {0x002D, "No Sync Error"},
+ {0x0030, "Invalid DC SYNCH configuration"},
+ {0x0031, "Invalid DC latch configuration"},
+ {0x0032, "PLL error"},
+ {0x0033, "DC Sync IO Error"},
+ {0x0034, "DC Sync Timeout Error"},
+ {0x0035, "DC Invalid Sync Cycle Time"},
+ {0x0036, "DC Sync0 Cycle Time"},
+ {0x0037, "DC Sync1 Cycle Time"},
+ {0x0041, "MBX_AOE"},
+ {0x0042, "MBX_EOE"},
+ {0x0043, "MBX_COE"},
+ {0x0044, "MBX_FOE"},
+ {0x0045, "MBX_SOE"},
+ {0x004F, "MBX_VOE"},
+ {0x0050, "EEPROM No Access"},
+ {0x0051, "EEPROM Error"},
+ {0x0060, "Slave Restarted Locally"},
+ {0xffff}
+};
+
+
+/*****************************************************************************/
+
+/**
+ Change state: CODE.
+*/
+
+void ec_fsm_change_state_code(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ uint32_t code;
+ const ec_code_msg_t *al_msg;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed to receive"
+ " AL status code datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(fsm->slave, "Reception of AL status code"
+ " datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ } else {
+ code = EC_READ_U16(datagram->data);
+ for (al_msg = al_status_messages; al_msg->code != 0xffff; al_msg++) {
+ if (al_msg->code != code) {
+ continue;
+ }
+
+ EC_SLAVE_ERR(fsm->slave, "AL status message 0x%04X: \"%s\".\n",
+ al_msg->code, al_msg->message);
+ break;
+ }
+ if (al_msg->code == 0xffff) { /* not found in our list. */
+ EC_SLAVE_ERR(fsm->slave, "Unknown AL status code 0x%04X.\n",
+ code);
+ }
+ }
+
+ // acknowledge "old" slave state
+ ec_datagram_fpwr(datagram, fsm->slave->station_address, 0x0120, 2);
+ EC_WRITE_U16(datagram->data, fsm->slave->current_state);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_change_state_ack;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: ACK.
+*/
+
+void ec_fsm_change_state_ack(ec_fsm_change_t *fsm /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive state ack datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Reception of state ack datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ fsm->take_time = 1;
+
+ // read new AL status
+ ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_change_state_check_ack;
+}
+
+/*****************************************************************************/
+
+/**
+ Change state: CHECK ACK.
+*/
+
+void ec_fsm_change_state_check_ack(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive state ack check datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Reception of state ack check datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ slave->current_state = EC_READ_U8(datagram->data);
+
+ if (!(slave->current_state & EC_SLAVE_STATE_ACK_ERR)) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str, 0);
+ if (fsm->mode == EC_FSM_CHANGE_MODE_FULL) {
+ fsm->state = ec_fsm_change_state_error;
+ }
+ else { // EC_FSM_CHANGE_MODE_ACK_ONLY
+ fsm->state = ec_fsm_change_state_end;
+ }
+ EC_SLAVE_INFO(slave, "Acknowledged state %s.\n", state_str);
+ return;
+ }
+
+ if (datagram->jiffies_received - fsm->jiffies_start >=
+ EC_AL_STATE_CHANGE_TIMEOUT * HZ) {
+ // timeout while checking
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str, 0);
+ fsm->state = ec_fsm_change_state_error;
+ EC_SLAVE_ERR(slave, "Timeout while acknowledging state %s.\n",
+ state_str);
+ return;
+ }
+
+ // reread new AL status
+ ec_datagram_fprd(datagram, slave->station_address, 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_change_state_error(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_change_state_end(ec_fsm_change_t *fsm
+ /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_change.h b/net/ethercat/master/fsm_change.h
new file mode 100644
index 000000000000..59a4d66a7594
--- /dev/null
+++ b/net/ethercat/master/fsm_change.h
@@ -0,0 +1,92 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 state change FSM.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_CHANGE_H__
+#define __EC_FSM_CHANGE_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "slave.h"
+
+/*****************************************************************************/
+
+/**
+ Mode of the change state machine.
+*/
+
+typedef enum {
+ EC_FSM_CHANGE_MODE_FULL, /**< full state change */
+ EC_FSM_CHANGE_MODE_ACK_ONLY /**< only state acknowledgement */
+}
+ec_fsm_change_mode_t;
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_change ec_fsm_change_t; /**< \see ec_fsm_change */
+
+/**
+ EtherCAT state change FSM.
+*/
+
+struct ec_fsm_change
+{
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+ unsigned int retries; /**< retries upon datagram timeout */
+
+ void (*state)(ec_fsm_change_t *); /**< slave state change state function */
+ ec_fsm_change_mode_t mode; /**< full state change, or ack only. */
+ ec_slave_state_t requested_state; /**< input: state */
+ ec_slave_state_t old_state; /**< prior slave state */
+ unsigned long jiffies_start; /**< change timer */
+ uint8_t take_time; /**< take sending timestamp */
+ uint8_t spontaneous_change; /**< spontaneous state change detected */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_change_init(ec_fsm_change_t *, ec_datagram_t *);
+void ec_fsm_change_clear(ec_fsm_change_t *);
+
+void ec_fsm_change_start(ec_fsm_change_t *, ec_slave_t *, ec_slave_state_t);
+void ec_fsm_change_ack(ec_fsm_change_t *, ec_slave_t *);
+
+int ec_fsm_change_exec(ec_fsm_change_t *);
+int ec_fsm_change_success(ec_fsm_change_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_coe.c b/net/ethercat/master/fsm_coe.c
new file mode 100644
index 000000000000..cea0b0239df3
--- /dev/null
+++ b/net/ethercat/master/fsm_coe.c
@@ -0,0 +1,2534 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CoE state machines.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "fsm_coe.h"
+#include "slave_config.h"
+
+/*****************************************************************************/
+
+/** Maximum time in ms to wait for responses when reading out the dictionary.
+ */
+#define EC_FSM_COE_DICT_TIMEOUT 1000
+
+/** CoE download request header size.
+ */
+#define EC_COE_DOWN_REQ_HEADER_SIZE 10
+
+/** CoE download segment request header size.
+ */
+#define EC_COE_DOWN_SEG_REQ_HEADER_SIZE 3
+
+/** Minimum size of download segment.
+ */
+#define EC_COE_DOWN_SEG_MIN_DATA_SIZE 7
+
+/** Enable debug output for CoE retries.
+ */
+#define DEBUG_RETRIES 0
+
+/** Enable warning output if transfers take too long.
+ */
+#define DEBUG_LONG 0
+
+/*****************************************************************************/
+
+void ec_fsm_coe_dict_start(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_response(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_desc_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_desc_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_desc_response(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_entry_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_entry_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_dict_entry_response(ec_fsm_coe_t *, ec_datagram_t *);
+
+void ec_fsm_coe_down_start(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_down_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_down_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_down_response(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_down_seg_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_down_seg_response(ec_fsm_coe_t *, ec_datagram_t *);
+
+void ec_fsm_coe_up_start(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_response(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_seg_request(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_seg_check(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_up_seg_response(ec_fsm_coe_t *, ec_datagram_t *);
+
+void ec_fsm_coe_end(ec_fsm_coe_t *, ec_datagram_t *);
+void ec_fsm_coe_error(ec_fsm_coe_t *, ec_datagram_t *);
+
+/*****************************************************************************/
+
+/** SDO abort messages.
+ *
+ * The "abort SDO transfer request" supplies an abort code, which can be
+ * translated to clear text. This table does the mapping of the codes and
+ * messages.
+ */
+const ec_code_msg_t sdo_abort_messages[] = {
+ {0x05030000, "Toggle bit not changed"},
+ {0x05040000, "SDO protocol timeout"},
+ {0x05040001, "Client/Server command specifier not valid or unknown"},
+ {0x05040005, "Out of memory"},
+ {0x06010000, "Unsupported access to an object"},
+ {0x06010001, "Attempt to read a write-only object"},
+ {0x06010002, "Attempt to write a read-only object"},
+ {0x06020000, "This object does not exist in the object directory"},
+ {0x06040041, "The object cannot be mapped into the PDO"},
+ {0x06040042, "The number and length of the objects to be mapped would"
+ " exceed the PDO length"},
+ {0x06040043, "General parameter incompatibility reason"},
+ {0x06040047, "Gerneral internal incompatibility in device"},
+ {0x06060000, "Access failure due to a hardware error"},
+ {0x06070010, "Data type does not match, length of service parameter does"
+ " not match"},
+ {0x06070012, "Data type does not match, length of service parameter too"
+ " high"},
+ {0x06070013, "Data type does not match, length of service parameter too"
+ " low"},
+ {0x06090011, "Subindex does not exist"},
+ {0x06090030, "Value range of parameter exceeded"},
+ {0x06090031, "Value of parameter written too high"},
+ {0x06090032, "Value of parameter written too low"},
+ {0x06090036, "Maximum value is less than minimum value"},
+ {0x08000000, "General error"},
+ {0x08000020, "Data cannot be transferred or stored to the application"},
+ {0x08000021, "Data cannot be transferred or stored to the application"
+ " because of local control"},
+ {0x08000022, "Data cannot be transferred or stored to the application"
+ " because of the present device state"},
+ {0x08000023, "Object dictionary dynamic generation fails or no object"
+ " dictionary is present"},
+ {}
+};
+
+/*****************************************************************************/
+
+/** Outputs an SDO abort message.
+ */
+void ec_canopen_abort_msg(
+ const ec_slave_t *slave, /**< Slave. */
+ uint32_t abort_code /**< Abort code to search for. */
+ )
+{
+ const ec_code_msg_t *abort_msg;
+
+ for (abort_msg = sdo_abort_messages; abort_msg->code; abort_msg++) {
+ if (abort_msg->code == abort_code) {
+ EC_SLAVE_ERR(slave, "SDO abort message 0x%08X: \"%s\".\n",
+ abort_msg->code, abort_msg->message);
+ return;
+ }
+ }
+
+ EC_SLAVE_ERR(slave, "Unknown SDO abort code 0x%08X.\n", abort_code);
+}
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_coe_init(
+ ec_fsm_coe_t *fsm /**< Finite state machine */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = NULL;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_coe_clear(
+ ec_fsm_coe_t *fsm /**< Finite state machine */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** Starts reading a slaves' SDO dictionary.
+ */
+void ec_fsm_coe_dictionary(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ ec_slave_t *slave /**< EtherCAT slave */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_coe_dict_start;
+}
+
+/*****************************************************************************/
+
+/** Starts to transfer an SDO to/from a slave.
+ */
+void ec_fsm_coe_transfer(
+ ec_fsm_coe_t *fsm, /**< State machine. */
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ ec_sdo_request_t *request /**< SDO request. */
+ )
+{
+ fsm->slave = slave;
+ fsm->request = request;
+
+ if (request->dir == EC_DIR_OUTPUT) {
+ fsm->state = ec_fsm_coe_down_start;
+ }
+ else {
+ fsm->state = ec_fsm_coe_up_start;
+ }
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * \return 1 if the datagram was used, else 0.
+ */
+int ec_fsm_coe_exec(
+ ec_fsm_coe_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_coe_end && fsm->state != ec_fsm_coe_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_coe_success(
+ const ec_fsm_coe_t *fsm /**< Finite state machine */
+ )
+{
+ return fsm->state == ec_fsm_coe_end;
+}
+
+/*****************************************************************************/
+
+/** Check if the received data are a CoE emergency request.
+ *
+ * If the check is positive, the emergency request is output.
+ *
+ * \return The data were an emergency request.
+ */
+int ec_fsm_coe_check_emergency(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ const uint8_t *data, /**< CoE mailbox data. */
+ size_t size /**< CoE mailbox data size. */
+ )
+{
+ if (size < 2 || ((EC_READ_U16(data) >> 12) & 0x0F) != 0x01)
+ return 0;
+
+ if (size < 10) {
+ EC_SLAVE_WARN(fsm->slave, "Received incomplete CoE Emergency"
+ " request:\n");
+ ec_print_data(data, size);
+ return 1;
+ }
+
+ {
+ ec_slave_config_t *sc = fsm->slave->config;
+ if (sc) {
+ ec_coe_emerg_ring_push(&sc->emerg_ring, data + 2);
+ }
+ }
+
+ EC_SLAVE_WARN(fsm->slave, "CoE Emergency Request received:\n"
+ "Error code 0x%04X, Error register 0x%02X, data:\n",
+ EC_READ_U16(data + 2), EC_READ_U8(data + 4));
+ ec_print_data(data + 5, 5);
+ return 1;
+}
+
+/******************************************************************************
+ * CoE dictionary state machine
+ *****************************************************************************/
+
+/** Prepare a dictionary request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_fsm_coe_prepare_dict(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8);
+ if (IS_ERR(data)) {
+ return PTR_ERR(data);
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x01); // Get OD List Request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, 0x0001); // deliver all SDOs!
+
+ fsm->state = ec_fsm_coe_dict_request;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** CoE state: DICT START.
+ */
+void ec_fsm_coe_dict_start(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support CoE!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (slave->sii.has_general && !slave->sii.coe_details.enable_sdo_info) {
+ EC_SLAVE_ERR(slave, "Slave does not support"
+ " SDO information service!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_coe_prepare_dict(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+}
+
+/*****************************************************************************/
+
+/** CoE state: DICT REQUEST.
+ * \todo Timeout behavior
+ */
+void ec_fsm_coe_dict_request(
+ ec_fsm_coe_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_coe_prepare_dict(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE dictionary"
+ " request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE dictionary request failed: ");
+ 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_coe_dict_check;
+}
+
+/*****************************************************************************/
+
+/** CoE state: DICT CHECK.
+ */
+void ec_fsm_coe_dict_check(
+ ec_fsm_coe_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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave,"Reception of CoE 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_FSM_COE_DICT_TIMEOUT) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for"
+ " SDO dictionary list response.\n");
+ 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_coe_dict_response;
+}
+
+/*****************************************************************************/
+
+/** Prepare an object description request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_fsm_coe_dict_prepare_desc(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ u8 *data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 8);
+ if (IS_ERR(data)) {
+ return PTR_ERR(data);
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x03); // Get object description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index
+
+ fsm->state = ec_fsm_coe_dict_desc_request;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_dict_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+ unsigned int sdo_count, i;
+ uint16_t sdo_index, fragments_left;
+ ec_sdo_t *sdo;
+ bool first_segment;
+ size_t index_list_offset;
+
+ 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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE dictionary"
+ " response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE dictionary 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_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_check;
+ return;
+ }
+
+ if (rec_size < 3) {
+ EC_SLAVE_ERR(slave, "Received corrupted SDO dictionary response"
+ " (size %zu).\n", rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8(data + 2) & 0x7F) == 0x07) { // error response
+ EC_SLAVE_ERR(slave, "SDO information error response!\n");
+ if (rec_size < 10) {
+ EC_SLAVE_ERR(slave, "Incomplete SDO information"
+ " error response:\n");
+ ec_print_data(data, rec_size);
+ } else {
+ ec_canopen_abort_msg(slave, EC_READ_U32(data + 6));
+ }
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) != 0x02) { // Get OD List response
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid SDO list response!"
+ " Retrying...\n");
+ ec_print_data(data, rec_size);
+ }
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_check;
+ return;
+ }
+
+ first_segment = list_empty(&slave->sdo_dictionary) ? true : false;
+ index_list_offset = first_segment ? 8 : 6;
+
+ if (rec_size < index_list_offset || rec_size % 2) {
+ EC_SLAVE_ERR(slave, "Invalid data size %zu!\n", rec_size);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ sdo_count = (rec_size - index_list_offset) / 2;
+
+ for (i = 0; i < sdo_count; i++) {
+ sdo_index = EC_READ_U16(data + index_list_offset + i * 2);
+ if (!sdo_index) {
+ EC_SLAVE_DBG(slave, 1, "SDO dictionary contains index 0x0000.\n");
+ continue;
+ }
+
+ if (!(sdo = (ec_sdo_t *) kmalloc(sizeof(ec_sdo_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate memory for SDO!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ ec_sdo_init(sdo, slave, sdo_index);
+ list_add_tail(&sdo->list, &slave->sdo_dictionary);
+ }
+
+ fragments_left = EC_READ_U16(data + 4);
+ if (fragments_left) {
+ EC_SLAVE_DBG(slave, 1, "SDO list fragments left: %u\n",
+ fragments_left);
+ }
+
+ if (EC_READ_U8(data + 2) & 0x80 || fragments_left) {
+ // more messages waiting. check again.
+ 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_coe_dict_check;
+ return;
+ }
+
+ if (list_empty(&slave->sdo_dictionary)) {
+ // no SDOs in dictionary. finished.
+ fsm->state = ec_fsm_coe_end; // success
+ return;
+ }
+
+ // fetch SDO descriptions
+ fsm->sdo = list_entry(slave->sdo_dictionary.next, ec_sdo_t, list);
+
+ fsm->retries = EC_FSM_RETRIES;
+ if (ec_fsm_coe_dict_prepare_desc(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC REQUEST.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_dict_desc_request(
+ ec_fsm_coe_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_coe_dict_prepare_desc(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE SDO"
+ " description request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE SDO description"
+ " request failed: ");
+ 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_coe_dict_desc_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC CHECK.
+*/
+
+void ec_fsm_coe_dict_desc_check(
+ ec_fsm_coe_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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE 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_FSM_COE_DICT_TIMEOUT) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for"
+ " SDO 0x%04x object description response.\n",
+ fsm->sdo->index);
+ 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_coe_dict_desc_response;
+}
+
+/*****************************************************************************/
+
+/** Prepare an entry description request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_fsm_coe_dict_prepare_entry(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ u8 *data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10);
+ if (IS_ERR(data)) {
+ return PTR_ERR(data);
+ }
+
+ EC_WRITE_U16(data, 0x8 << 12); // SDO information
+ EC_WRITE_U8 (data + 2, 0x05); // Get entry description request
+ EC_WRITE_U8 (data + 3, 0x00);
+ EC_WRITE_U16(data + 4, 0x0000);
+ EC_WRITE_U16(data + 6, fsm->sdo->index); // SDO index
+ EC_WRITE_U8 (data + 8, fsm->subindex); // SDO subindex
+ EC_WRITE_U8 (data + 9, 0x01); // value info (access rights only)
+
+ fsm->state = ec_fsm_coe_dict_entry_request;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT DESC RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_dict_desc_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_t *sdo = fsm->sdo;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, name_size;
+
+ 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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE SDO description"
+ " response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE SDO description"
+ " 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_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_desc_check;
+ return;
+ }
+
+ if (rec_size < 3) {
+ EC_SLAVE_ERR(slave, "Received corrupted SDO description response"
+ " (size %zu).\n", rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
+ EC_SLAVE_ERR(slave, "SDO information error response while"
+ " fetching SDO 0x%04X!\n", sdo->index);
+ ec_canopen_abort_msg(slave, EC_READ_U32(data + 6));
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (rec_size < 8) {
+ EC_SLAVE_ERR(slave, "Received corrupted SDO"
+ " description response (size %zu).\n", rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) != 0x04 || // Object desc. response
+ EC_READ_U16(data + 6) != sdo->index) { // SDO index
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid object description response while"
+ " fetching SDO 0x%04X!\n", sdo->index);
+ ec_print_data(data, rec_size);
+ }
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_desc_check;
+ return;
+ }
+
+ if (rec_size < 12) {
+ EC_SLAVE_ERR(slave, "Invalid data size!\n");
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ sdo->max_subindex = EC_READ_U8(data + 10);
+ sdo->object_code = EC_READ_U8(data + 11);
+
+ name_size = rec_size - 12;
+ if (name_size) {
+ if (!(sdo->name = kmalloc(name_size + 1, GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate SDO name!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ memcpy(sdo->name, data + 12, name_size);
+ sdo->name[name_size] = 0;
+ }
+
+ if (EC_READ_U8(data + 2) & 0x80) {
+ EC_SLAVE_ERR(slave, "Fragment follows (not implemented)!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ // start fetching entries
+
+ fsm->subindex = 0;
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_coe_dict_prepare_entry(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY REQUEST.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_dict_entry_request(
+ ec_fsm_coe_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_coe_dict_prepare_entry(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE SDO entry"
+ " request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE SDO entry request failed: ");
+ 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_coe_dict_entry_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY CHECK.
+*/
+
+void ec_fsm_coe_dict_entry_check(
+ ec_fsm_coe_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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE 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_FSM_COE_DICT_TIMEOUT) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for"
+ " SDO entry 0x%04x:%x description response.\n",
+ fsm->sdo->index, fsm->subindex);
+ 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_coe_dict_entry_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DICT ENTRY RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_dict_entry_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_t *sdo = fsm->sdo;
+ uint8_t *data, mbox_prot;
+ size_t rec_size, data_size;
+ ec_sdo_entry_t *entry;
+ u16 word;
+
+ 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_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE SDO"
+ " description response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE SDO description"
+ " 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_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_SLAVE_ERR(slave, "Received mailbox protocol"
+ " 0x%02X as response.\n", mbox_prot);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_entry_check;
+ return;
+ }
+
+ if (rec_size < 3) {
+ EC_SLAVE_ERR(slave, "Received corrupted SDO entry"
+ " description response (size %zu).\n", rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x8 && // SDO information
+ (EC_READ_U8 (data + 2) & 0x7F) == 0x07) { // error response
+ EC_SLAVE_WARN(slave, "SDO information error response while"
+ " fetching SDO entry 0x%04X:%02X!\n",
+ sdo->index, fsm->subindex);
+ ec_canopen_abort_msg(slave, EC_READ_U32(data + 6));
+
+ /* There may be gaps in the subindices, so try to continue with next
+ * subindex. */
+
+ } else {
+
+ if (rec_size < 9) {
+ EC_SLAVE_ERR(slave, "Received corrupted SDO entry"
+ " description response (size %zu).\n", rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x8 || // SDO information
+ (EC_READ_U8(data + 2) & 0x7F) != 0x06 || // Entry desc. response
+ EC_READ_U16(data + 6) != sdo->index || // SDO index
+ EC_READ_U8(data + 8) != fsm->subindex) { // SDO subindex
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid entry description response"
+ " while fetching SDO entry 0x%04X:%02X!\n",
+ sdo->index, fsm->subindex);
+ ec_print_data(data, rec_size);
+ }
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_dict_entry_check;
+ return;
+ }
+
+ if (rec_size < 16) {
+ EC_SLAVE_ERR(slave, "Invalid data size %zu!\n", rec_size);
+ ec_print_data(data, rec_size);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ data_size = rec_size - 16;
+
+ if (!(entry = (ec_sdo_entry_t *)
+ kmalloc(sizeof(ec_sdo_entry_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate entry!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ ec_sdo_entry_init(entry, sdo, fsm->subindex);
+ entry->data_type = EC_READ_U16(data + 10);
+ entry->bit_length = EC_READ_U16(data + 12);
+
+ // read access rights
+ word = EC_READ_U16(data + 14);
+ entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP] = word & 0x0001;
+ entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ (word >> 1) & 0x0001;
+ entry->read_access[EC_SDO_ENTRY_ACCESS_OP] = (word >> 2) & 0x0001;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP] = (word >> 3) & 0x0001;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ (word >> 4) & 0x0001;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_OP] = (word >> 5) & 0x0001;
+
+ if (data_size) {
+ uint8_t *desc;
+ if (!(desc = kmalloc(data_size + 1, GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate SDO entry name!\n");
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+ memcpy(desc, data + 16, data_size);
+ desc[data_size] = 0;
+ entry->description = desc;
+ }
+
+ list_add_tail(&entry->list, &sdo->entries);
+ }
+
+ if (fsm->subindex < sdo->max_subindex) {
+
+ fsm->subindex++;
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_coe_dict_prepare_entry(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+
+ return;
+ }
+
+ // another SDO description to fetch?
+ if (fsm->sdo->list.next != &slave->sdo_dictionary) {
+
+ fsm->sdo = list_entry(fsm->sdo->list.next, ec_sdo_t, list);
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_coe_dict_prepare_desc(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+
+ return;
+ }
+
+ fsm->state = ec_fsm_coe_end;
+}
+
+/******************************************************************************
+ * CoE state machine
+ *****************************************************************************/
+
+/** Prepare a donwnload request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_fsm_coe_prepare_down_start(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ u8 *data;
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->request;
+ uint8_t data_set_size;
+
+ if (request->data_size <= 4) { // use expedited transfer type
+ data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
+ EC_COE_DOWN_REQ_HEADER_SIZE);
+ if (IS_ERR(data)) {
+ request->errno = PTR_ERR(data);
+ return PTR_ERR(data);
+ }
+
+ fsm->remaining = 0;
+
+ data_set_size = 4 - request->data_size;
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, (0x3 // size specified, expedited
+ | data_set_size << 2
+ | ((request->complete_access ? 1 : 0) << 4)
+ | 0x1 << 5)); // Download request
+ EC_WRITE_U16(data + 3, request->index);
+ EC_WRITE_U8 (data + 5,
+ request->complete_access ? 0x00 : request->subindex);
+ memcpy(data + 6, request->data, request->data_size);
+ memset(data + 6 + request->data_size, 0x00, 4 - request->data_size);
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Expedited download request:\n");
+ ec_print_data(data, EC_COE_DOWN_REQ_HEADER_SIZE);
+ }
+ }
+ else { // request->data_size > 4, use normal transfer type
+ size_t data_size,
+ max_data_size =
+ slave->configured_rx_mailbox_size - EC_MBOX_HEADER_SIZE,
+ required_data_size =
+ EC_COE_DOWN_REQ_HEADER_SIZE + request->data_size;
+
+ if (max_data_size < required_data_size) {
+ // segmenting needed
+ data_size = max_data_size;
+ } else {
+ data_size = required_data_size;
+ }
+
+ data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
+ data_size);
+ if (IS_ERR(data)) {
+ request->errno = PTR_ERR(data);
+ return PTR_ERR(data);
+ }
+
+ fsm->offset = 0;
+ fsm->remaining = request->data_size;
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8(data + 2,
+ 0x1 // size indicator, normal
+ | ((request->complete_access ? 1 : 0) << 4)
+ | 0x1 << 5); // Download request
+ EC_WRITE_U16(data + 3, request->index);
+ EC_WRITE_U8 (data + 5,
+ request->complete_access ? 0x00 : request->subindex);
+ EC_WRITE_U32(data + 6, request->data_size);
+
+ if (data_size > EC_COE_DOWN_REQ_HEADER_SIZE) {
+ size_t segment_size = data_size - EC_COE_DOWN_REQ_HEADER_SIZE;
+ memcpy(data + EC_COE_DOWN_REQ_HEADER_SIZE,
+ request->data, segment_size);
+ fsm->offset += segment_size;
+ fsm->remaining -= segment_size;
+ }
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Normal download request:\n");
+ ec_print_data(data, data_size);
+ }
+ }
+
+ fsm->state = ec_fsm_coe_down_request;
+ return 0;
+}
+
+/****************************************************************************/
+
+/** CoE state: DOWN START.
+ */
+void ec_fsm_coe_down_start(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->request;
+
+ if (fsm->slave->master->debug_level) {
+ char subidxstr[10];
+ if (request->complete_access) {
+ subidxstr[0] = 0x00;
+ } else {
+ sprintf(subidxstr, ":%02X", request->subindex);
+ }
+ EC_SLAVE_DBG(slave, 1, "Downloading SDO 0x%04X%s.\n",
+ request->index, subidxstr);
+ ec_print_data(request->data, request->data_size);
+ }
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support CoE!\n");
+ request->errno = EPROTONOSUPPORT;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (slave->configured_rx_mailbox_size <
+ EC_MBOX_HEADER_SIZE + EC_COE_DOWN_REQ_HEADER_SIZE) {
+ EC_SLAVE_ERR(slave, "Mailbox too small!\n");
+ request->errno = EOVERFLOW;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+
+ fsm->request->jiffies_sent = jiffies;
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_coe_prepare_down_start(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN REQUEST.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_down_request(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ unsigned long diff_ms;
+
+ if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ if (ec_fsm_coe_prepare_down_start(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE download"
+ " request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ;
+
+ if (fsm->datagram->working_counter != 1) {
+ if (!fsm->datagram->working_counter) {
+ if (diff_ms < fsm->request->response_timeout) {
+#if DEBUG_RETRIES
+ EC_SLAVE_DBG(slave, 1, "Slave did not respond to SDO"
+ " download request. Retrying after %lu ms...\n",
+ diff_ms);
+#endif
+ // no response; send request datagram again
+ if (ec_fsm_coe_prepare_down_start(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+ }
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE download request"
+ " for SDO 0x%04x:%x failed with timeout after %lu ms: ",
+ fsm->request->index, fsm->request->subindex, diff_ms);
+ ec_datagram_print_wc_error(fsm->datagram);
+ return;
+ }
+
+#if DEBUG_LONG
+ if (diff_ms > 200) {
+ EC_SLAVE_WARN(slave, "SDO 0x%04x:%x download took %lu ms.\n",
+ fsm->request->index, fsm->request->subindex, diff_ms);
+ }
+#endif
+
+ 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_coe_down_check;
+}
+
+/*****************************************************************************/
+
+/** CoE state: DOWN CHECK.
+ */
+void ec_fsm_coe_down_check(
+ ec_fsm_coe_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->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check"
+ " datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE 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 >= fsm->request->response_timeout) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting"
+ " for SDO 0x%04x:%x download response.\n", diff_ms,
+ fsm->request->index, fsm->request->subindex);
+ 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_coe_down_response;
+}
+
+/*****************************************************************************/
+
+/** Prepare a download segment request.
+ */
+void ec_fsm_coe_down_prepare_segment_request(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->request;
+ size_t max_segment_size =
+ slave->configured_rx_mailbox_size
+ - EC_MBOX_HEADER_SIZE
+ - EC_COE_DOWN_SEG_REQ_HEADER_SIZE;
+ size_t data_size;
+ uint8_t last_segment, seg_data_size, *data;
+
+ if (fsm->remaining > max_segment_size) {
+ fsm->segment_size = max_segment_size;
+ last_segment = 0;
+ } else {
+ fsm->segment_size = fsm->remaining;
+ last_segment = 1;
+ }
+
+ if (fsm->segment_size > EC_COE_DOWN_SEG_MIN_DATA_SIZE) {
+ seg_data_size = 0x00;
+ data_size = EC_COE_DOWN_SEG_REQ_HEADER_SIZE + fsm->segment_size;
+ } else {
+ seg_data_size = EC_COE_DOWN_SEG_MIN_DATA_SIZE - fsm->segment_size;
+ data_size = EC_COE_DOWN_SEG_REQ_HEADER_SIZE
+ + EC_COE_DOWN_SEG_MIN_DATA_SIZE;
+ }
+
+ data = ec_slave_mbox_prepare_send(slave, datagram, 0x03,
+ data_size);
+ if (IS_ERR(data)) {
+ request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8(data + 2, (last_segment ? 1 : 0)
+ | (seg_data_size << 1)
+ | (fsm->toggle << 4)
+ | (0x00 << 5)); // Download segment request
+ memcpy(data + EC_COE_DOWN_SEG_REQ_HEADER_SIZE,
+ request->data + fsm->offset, fsm->segment_size);
+ if (fsm->segment_size < EC_COE_DOWN_SEG_MIN_DATA_SIZE) {
+ memset(data + EC_COE_DOWN_SEG_REQ_HEADER_SIZE + fsm->segment_size,
+ 0x00, EC_COE_DOWN_SEG_MIN_DATA_SIZE - fsm->segment_size);
+ }
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Download segment request:\n");
+ ec_print_data(data, data_size);
+ }
+
+ fsm->state = ec_fsm_coe_down_seg_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_down_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+ ec_sdo_request_t *request = 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) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE download"
+ " response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE download 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)) {
+ request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_down_check;
+ return;
+ }
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Download response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (rec_size < 6) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received data are too small (%zu bytes):\n",
+ rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ char subidxstr[10];
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ if (request->complete_access) {
+ subidxstr[0] = 0x00;
+ } else {
+ sprintf(subidxstr, ":%02X", request->subindex);
+ }
+ EC_SLAVE_ERR(slave, "SDO download 0x%04X%s (%zu bytes) aborted.\n",
+ request->index, subidxstr, request->data_size);
+ if (rec_size < 10) {
+ EC_SLAVE_ERR(slave, "Incomplete abort command:\n");
+ ec_print_data(data, rec_size);
+ } else {
+ fsm->request->abort_code = EC_READ_U32(data + 6);
+ ec_canopen_abort_msg(slave, fsm->request->abort_code);
+ }
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8 (data + 2) >> 5 != 0x3 || // Download response
+ EC_READ_U16(data + 3) != request->index || // index
+ EC_READ_U8 (data + 5) != request->subindex) { // subindex
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid SDO download response!"
+ " Retrying...\n");
+ ec_print_data(data, rec_size);
+ }
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_down_check;
+ return;
+ }
+
+ if (fsm->remaining) { // more segments to download
+ fsm->toggle = 0;
+ ec_fsm_coe_down_prepare_segment_request(fsm, datagram);
+ } else {
+ fsm->state = ec_fsm_coe_end; // success
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN SEG CHECK.
+*/
+
+void ec_fsm_coe_down_seg_check(
+ ec_fsm_coe_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--)
+ return;
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE mailbox segment 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 >= fsm->request->response_timeout) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for SDO download"
+ " segment response.\n");
+ 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_coe_down_seg_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: DOWN SEG RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_down_seg_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+ ec_sdo_request_t *request = 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) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE download response"
+ " datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE download 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)) {
+ request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_down_check;
+ return;
+ }
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Download response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (rec_size < 6) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received data are too small (%zu bytes):\n",
+ rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ char subidxstr[10];
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ if (request->complete_access) {
+ subidxstr[0] = 0x00;
+ } else {
+ sprintf(subidxstr, ":%02X", request->subindex);
+ }
+ EC_SLAVE_ERR(slave, "SDO download 0x%04X%s (%zu bytes) aborted.\n",
+ request->index, subidxstr, request->data_size);
+ if (rec_size < 10) {
+ EC_SLAVE_ERR(slave, "Incomplete abort command:\n");
+ ec_print_data(data, rec_size);
+ } else {
+ fsm->request->abort_code = EC_READ_U32(data + 6);
+ ec_canopen_abort_msg(slave, fsm->request->abort_code);
+ }
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 ||
+ ((EC_READ_U8(data + 2) >> 5) != 0x01)) { // segment response
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid SDO download response!"
+ " Retrying...\n");
+ ec_print_data(data, rec_size);
+ }
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_down_seg_check;
+ return;
+ }
+
+ if (((EC_READ_U8(data + 2) >> 4) & 0x01) != fsm->toggle) {
+ EC_SLAVE_ERR(slave, "Invalid toggle received during"
+ " segmented download:\n");
+ ec_print_data(data, rec_size);
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ fsm->offset += fsm->segment_size;
+ fsm->remaining -= fsm->segment_size;
+
+ if (fsm->remaining) { // more segments to download
+ fsm->toggle = !fsm->toggle;
+ ec_fsm_coe_down_prepare_segment_request(fsm, datagram);
+ } else {
+ fsm->state = ec_fsm_coe_end; // success
+ }
+}
+
+/*****************************************************************************/
+
+/** Prepare an upload request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_fsm_coe_prepare_up(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->request;
+ ec_master_t *master = slave->master;
+
+ u8 *data = ec_slave_mbox_prepare_send(slave, datagram, 0x03, 10);
+ if (IS_ERR(data)) {
+ request->errno = PTR_ERR(data);
+ return PTR_ERR(data);
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, 0x2 << 5); // initiate upload request
+ EC_WRITE_U16(data + 3, request->index);
+ EC_WRITE_U8 (data + 5, request->subindex);
+ memset(data + 6, 0x00, 4);
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Upload request:\n");
+ ec_print_data(data, 10);
+ }
+
+ fsm->state = ec_fsm_coe_up_request;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP START.
+*/
+
+void ec_fsm_coe_up_start(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->request;
+
+ EC_SLAVE_DBG(slave, 1, "Uploading SDO 0x%04X:%02X.\n",
+ request->index, request->subindex);
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support CoE!\n");
+ request->errno = EPROTONOSUPPORT;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->request->jiffies_sent = jiffies;
+
+ if (ec_fsm_coe_prepare_up(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+}
+
+/*****************************************************************************/
+/**
+ CoE state: UP REQUEST.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_up_request(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ unsigned long diff_ms;
+
+ if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ if (ec_fsm_coe_prepare_up(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE upload request: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ;
+
+ if (fsm->datagram->working_counter != 1) {
+ if (!fsm->datagram->working_counter) {
+ if (diff_ms < fsm->request->response_timeout) {
+#if DEBUG_RETRIES
+ EC_SLAVE_DBG(slave, 1, "Slave did not respond to"
+ " SDO upload request. Retrying after %lu ms...\n",
+ diff_ms);
+#endif
+ // no response; send request datagram again
+ if (ec_fsm_coe_prepare_up(fsm, datagram)) {
+ fsm->state = ec_fsm_coe_error;
+ }
+ return;
+ }
+ }
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE upload request for"
+ " SDO 0x%04x:%x failed with timeout after %lu ms: ",
+ fsm->request->index, fsm->request->subindex, diff_ms);
+ ec_datagram_print_wc_error(fsm->datagram);
+ return;
+ }
+
+#if DEBUG_LONG
+ if (diff_ms > 200) {
+ EC_SLAVE_WARN(slave, "SDO 0x%04x:%x upload took %lu ms.\n",
+ fsm->request->index, fsm->request->subindex, diff_ms);
+ }
+#endif
+
+ 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_coe_up_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP CHECK.
+*/
+
+void ec_fsm_coe_up_check(
+ ec_fsm_coe_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->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE 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 >= fsm->request->response_timeout) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting for"
+ " SDO 0x%04x:%x upload response.\n", diff_ms,
+ fsm->request->index, fsm->request->subindex);
+ 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_coe_up_response;
+}
+
+/*****************************************************************************/
+
+/** Prepare an SDO upload segment request.
+ */
+void ec_fsm_coe_up_prepare_segment_request(
+ ec_fsm_coe_t *fsm, /**< Finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ uint8_t *data =
+ ec_slave_mbox_prepare_send(fsm->slave, datagram, 0x03, 10);
+ if (IS_ERR(data)) {
+ fsm->request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ EC_WRITE_U16(data, 0x2 << 12); // SDO request
+ EC_WRITE_U8 (data + 2, (fsm->toggle << 4 // toggle
+ | 0x3 << 5)); // upload segment request
+ memset(data + 3, 0x00, 7);
+
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(fsm->slave, 1, "Upload segment request:\n");
+ ec_print_data(data, 10);
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_up_response(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ uint16_t rec_index;
+ uint8_t *data, mbox_prot, rec_subindex;
+ size_t rec_size, data_size;
+ ec_sdo_request_t *request = fsm->request;
+ unsigned int expedited, size_specified;
+ int ret;
+
+ 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) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE upload response"
+ " datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE upload 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)) {
+ request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Upload response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_WARN(slave, "Received mailbox protocol 0x%02X"
+ " as response.\n", mbox_prot);
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_check;
+ return;
+ }
+
+ if (rec_size < 6) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received currupted SDO upload response"
+ " (%zu bytes)!\n", rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8(data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ EC_SLAVE_ERR(slave, "SDO upload 0x%04X:%02X aborted.\n",
+ request->index, request->subindex);
+ if (rec_size >= 10) {
+ request->abort_code = EC_READ_U32(data + 6);
+ ec_canopen_abort_msg(slave, request->abort_code);
+ } else {
+ EC_SLAVE_ERR(slave, "No abort message.\n");
+ }
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8(data + 2) >> 5 != 0x2) { // upload response
+ EC_SLAVE_ERR(slave, "Received unknown response while"
+ " uploading SDO 0x%04X:%02X.\n",
+ request->index, request->subindex);
+ ec_print_data(data, rec_size);
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ rec_index = EC_READ_U16(data + 3);
+ rec_subindex = EC_READ_U8(data + 5);
+
+ if (rec_index != request->index || rec_subindex != request->subindex) {
+ EC_SLAVE_ERR(slave, "Received upload response for wrong SDO"
+ " (0x%04X:%02X, requested: 0x%04X:%02X).\n",
+ rec_index, rec_subindex, request->index, request->subindex);
+ ec_print_data(data, rec_size);
+
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_check;
+ return;
+ }
+
+ // normal or expedited?
+ expedited = EC_READ_U8(data + 2) & 0x02;
+
+ if (expedited) {
+ size_specified = EC_READ_U8(data + 2) & 0x01;
+ if (size_specified) {
+ fsm->complete_size = 4 - ((EC_READ_U8(data + 2) & 0x0C) >> 2);
+ } else {
+ fsm->complete_size = 4;
+ }
+
+ if (rec_size < 6 + fsm->complete_size) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received corrupted SDO expedited upload"
+ " response (only %zu bytes)!\n", rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ ret = ec_sdo_request_copy_data(request, data + 6, fsm->complete_size);
+ if (ret) {
+ request->errno = -ret;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+ } else { // normal
+ if (rec_size < 10) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Received currupted SDO normal upload"
+ " response (only %zu bytes)!\n", rec_size);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ data_size = rec_size - 10;
+ fsm->complete_size = EC_READ_U32(data + 6);
+
+ if (!fsm->complete_size) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "No complete size supplied!\n");
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ ret = ec_sdo_request_alloc(request, fsm->complete_size);
+ if (ret) {
+ request->errno = -ret;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ ret = ec_sdo_request_copy_data(request, data + 10, data_size);
+ if (ret) {
+ request->errno = -ret;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ fsm->toggle = 0;
+
+ if (data_size < fsm->complete_size) {
+ EC_SLAVE_DBG(slave, 1, "SDO data incomplete (%zu / %u)."
+ " Segmenting...\n", data_size, fsm->complete_size);
+ ec_fsm_coe_up_prepare_segment_request(fsm, datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_seg_request;
+ return;
+ }
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Uploaded data:\n");
+ ec_print_data(request->data, request->data_size);
+ }
+
+ fsm->state = ec_fsm_coe_end; // success
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP REQUEST.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_up_seg_request(
+ ec_fsm_coe_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_fsm_coe_up_prepare_segment_request(fsm, datagram);
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE upload segment"
+ " request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE upload segment"
+ " request failed: ");
+ 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_coe_up_seg_check;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP CHECK.
+*/
+
+void ec_fsm_coe_up_seg_check(
+ ec_fsm_coe_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->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE mailbox check"
+ " datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE 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 >= fsm->request->response_timeout) {
+ fsm->request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for SDO upload"
+ " segment response.\n");
+ 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_coe_up_seg_response;
+}
+
+/*****************************************************************************/
+
+/**
+ CoE state: UP RESPONSE.
+ \todo Timeout behavior
+*/
+
+void ec_fsm_coe_up_seg_response(
+ ec_fsm_coe_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;
+ size_t rec_size, data_size;
+ ec_sdo_request_t *request = fsm->request;
+ unsigned int last_segment;
+
+ 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) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive CoE upload segment"
+ " response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ EC_SLAVE_ERR(slave, "Reception of CoE upload segment"
+ " 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)) {
+ request->errno = PTR_ERR(data);
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Upload segment response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != 0x03) { // CoE
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (ec_fsm_coe_check_emergency(fsm, data, rec_size)) {
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_seg_check;
+ return;
+ }
+
+ if (rec_size < 10) {
+ EC_SLAVE_ERR(slave, "Received currupted SDO upload"
+ " segment response!\n");
+ ec_print_data(data, rec_size);
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 == 0x2 && // SDO request
+ EC_READ_U8 (data + 2) >> 5 == 0x4) { // abort SDO transfer request
+ EC_SLAVE_ERR(slave, "SDO upload 0x%04X:%02X aborted.\n",
+ request->index, request->subindex);
+ request->abort_code = EC_READ_U32(data + 6);
+ ec_canopen_abort_msg(slave, request->abort_code);
+ request->errno = EIO;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ if (EC_READ_U16(data) >> 12 != 0x3 || // SDO response
+ EC_READ_U8 (data + 2) >> 5 != 0x0) { // upload segment response
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Invalid SDO upload segment response!\n");
+ ec_print_data(data, rec_size);
+ }
+ // check for CoE response again
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_seg_check;
+ return;
+ }
+
+ data_size = rec_size - 3; /* Header of segment upload is smaller than
+ normal upload */
+ if (rec_size == 10) {
+ uint8_t seg_size = (EC_READ_U8(data + 2) & 0xE) >> 1;
+ data_size -= seg_size;
+ }
+
+ if (request->data_size + data_size > fsm->complete_size) {
+ EC_SLAVE_ERR(slave, "SDO upload 0x%04X:%02X failed: Fragment"
+ " exceeding complete size!\n",
+ request->index, request->subindex);
+ request->errno = EOVERFLOW;
+ fsm->state = ec_fsm_coe_error;
+ return;
+ }
+
+ memcpy(request->data + request->data_size, data + 3, data_size);
+ request->data_size += data_size;
+
+ last_segment = EC_READ_U8(data + 2) & 0x01;
+ if (!last_segment) {
+ fsm->toggle = !fsm->toggle;
+ ec_fsm_coe_up_prepare_segment_request(fsm, datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_coe_up_seg_request;
+ return;
+ }
+
+ if (request->data_size != fsm->complete_size) {
+ EC_SLAVE_WARN(slave, "SDO upload 0x%04X:%02X: Assembled data"
+ " size (%zu) does not match complete size (%u)!\n",
+ request->index, request->subindex,
+ request->data_size, fsm->complete_size);
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Uploaded data:\n");
+ ec_print_data(request->data, request->data_size);
+ }
+
+ fsm->state = ec_fsm_coe_end; // success
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_coe_error(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_coe_end(
+ ec_fsm_coe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_coe.h b/net/ethercat/master/fsm_coe.h
new file mode 100644
index 000000000000..7b565040fd16
--- /dev/null
+++ b/net/ethercat/master/fsm_coe.h
@@ -0,0 +1,82 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CoE state machines.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_COE_H__
+#define __EC_FSM_COE_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "slave.h"
+#include "sdo.h"
+#include "sdo_request.h"
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_coe ec_fsm_coe_t; /**< \see ec_fsm_coe */
+
+/** Finite state machines for the CANopen over EtherCAT protocol.
+ */
+struct ec_fsm_coe {
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ unsigned int retries; /**< retries upon datagram timeout */
+
+ void (*state)(ec_fsm_coe_t *, ec_datagram_t *); /**< CoE state function */
+ ec_datagram_t *datagram; /**< Datagram used in last step. */
+ unsigned long jiffies_start; /**< CoE timestamp. */
+ ec_sdo_t *sdo; /**< current SDO */
+ uint8_t subindex; /**< current subindex */
+ ec_sdo_request_t *request; /**< SDO request */
+ uint32_t complete_size; /**< Used when segmenting. */
+ uint8_t toggle; /**< toggle bit for segment commands */
+ uint32_t offset; /**< Data offset during segmented download. */
+ uint32_t remaining; /**< Remaining bytes during segmented download. */
+ size_t segment_size; /**< Current segment size. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_coe_init(ec_fsm_coe_t *);
+void ec_fsm_coe_clear(ec_fsm_coe_t *);
+
+void ec_fsm_coe_dictionary(ec_fsm_coe_t *, ec_slave_t *);
+void ec_fsm_coe_transfer(ec_fsm_coe_t *, ec_slave_t *, ec_sdo_request_t *);
+
+int ec_fsm_coe_exec(ec_fsm_coe_t *, ec_datagram_t *);
+int ec_fsm_coe_success(const ec_fsm_coe_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_foe.c b/net/ethercat/master/fsm_foe.c
new file mode 100644
index 000000000000..2e8896998283
--- /dev/null
+++ b/net/ethercat/master/fsm_foe.c
@@ -0,0 +1,927 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2008 Olav Zarges, imc Messsysteme GmbH
+ * 2013 Florian Pose <fp@igh-essen.com>
+ *
+ * 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 FoE state machines.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "fsm_foe.h"
+#include "foe.h"
+
+/*****************************************************************************/
+
+/** Maximum time in ms to wait for responses when reading out the dictionary.
+ */
+#define EC_FSM_FOE_TIMEOUT 3000
+
+/** Mailbox type FoE.
+ */
+#define EC_MBOX_TYPE_FILEACCESS 0x04
+
+/** Size of the FoE header.
+ */
+#define EC_FOE_HEADER_SIZE 6
+// uint8_t OpCode
+// uint8_t reserved
+// uint32_t PacketNo, Password, ErrorCode
+
+//#define DEBUG_FOE
+
+/*****************************************************************************/
+
+/** FoE OpCodes.
+ */
+enum {
+ EC_FOE_OPCODE_RRQ = 1, /**< Read request. */
+ EC_FOE_OPCODE_WRQ = 2, /**< Write request. */
+ EC_FOE_OPCODE_DATA = 3, /**< Data. */
+ EC_FOE_OPCODE_ACK = 4, /**< Acknowledge. */
+ EC_FOE_OPCODE_ERR = 5, /**< Error. */
+ EC_FOE_OPCODE_BUSY = 6 /**< Busy. */
+} ec_foe_opcode_t;
+
+/*****************************************************************************/
+
+int ec_foe_prepare_data_send(ec_fsm_foe_t *, ec_datagram_t *);
+int ec_foe_prepare_wrq_send(ec_fsm_foe_t *, ec_datagram_t *);
+int ec_foe_prepare_rrq_send(ec_fsm_foe_t *, ec_datagram_t *);
+int ec_foe_prepare_send_ack(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_foe_set_tx_error(ec_fsm_foe_t *, uint32_t);
+void ec_foe_set_rx_error(ec_fsm_foe_t *, uint32_t);
+
+void ec_fsm_foe_end(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_error(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_fsm_foe_state_wrq_sent(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_state_rrq_sent(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_fsm_foe_state_ack_check(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_state_ack_read(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_fsm_foe_state_data_sent(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_fsm_foe_state_data_check(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_state_data_read(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_state_sent_ack(ec_fsm_foe_t *, ec_datagram_t *);
+
+void ec_fsm_foe_write_start(ec_fsm_foe_t *, ec_datagram_t *);
+void ec_fsm_foe_read_start(ec_fsm_foe_t *, ec_datagram_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_foe_init(
+ ec_fsm_foe_t *fsm /**< finite state machine */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = NULL;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_foe_clear(ec_fsm_foe_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * \return 1, if the datagram was used, else 0.
+ */
+int ec_fsm_foe_exec(
+ ec_fsm_foe_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_foe_end && fsm->state != ec_fsm_foe_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_foe_success(const ec_fsm_foe_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_foe_end;
+}
+
+/*****************************************************************************/
+
+/** Prepares an FoE transfer.
+ */
+void ec_fsm_foe_transfer(
+ ec_fsm_foe_t *fsm, /**< State machine. */
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ ec_foe_request_t *request /**< Sdo request. */
+ )
+{
+ fsm->slave = slave;
+ fsm->request = request;
+
+ if (request->dir == EC_DIR_OUTPUT) {
+ fsm->tx_buffer = fsm->request->buffer;
+ fsm->tx_buffer_size = fsm->request->data_size;
+ fsm->tx_buffer_offset = 0;
+
+ fsm->tx_filename = fsm->request->file_name;
+ fsm->tx_filename_len = strlen(fsm->tx_filename);
+
+ fsm->state = ec_fsm_foe_write_start;
+ }
+ else {
+ fsm->rx_buffer = fsm->request->buffer;
+ fsm->rx_buffer_size = fsm->request->buffer_size;
+
+ fsm->rx_filename = fsm->request->file_name;
+ fsm->rx_filename_len = strlen(fsm->rx_filename);
+
+ fsm->state = ec_fsm_foe_read_start;
+ }
+}
+
+/*****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_foe_error(
+ ec_fsm_foe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_foe_end(
+ ec_fsm_foe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+}
+
+/*****************************************************************************/
+
+/** Sends a file or the next fragment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_prepare_data_send(
+ ec_fsm_foe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ size_t remaining_size, current_size;
+ uint8_t *data;
+
+ remaining_size = fsm->tx_buffer_size - fsm->tx_buffer_offset;
+
+ if (remaining_size < fsm->slave->configured_tx_mailbox_size
+ - EC_MBOX_HEADER_SIZE - EC_FOE_HEADER_SIZE) {
+ current_size = remaining_size;
+ fsm->tx_last_packet = 1;
+ } else {
+ current_size = fsm->slave->configured_tx_mailbox_size
+ - EC_MBOX_HEADER_SIZE - EC_FOE_HEADER_SIZE;
+ }
+
+ data = ec_slave_mbox_prepare_send(fsm->slave,
+ datagram, EC_MBOX_TYPE_FILEACCESS,
+ current_size + EC_FOE_HEADER_SIZE);
+ if (IS_ERR(data)) {
+ return -1;
+ }
+
+ EC_WRITE_U16(data, EC_FOE_OPCODE_DATA); // OpCode = DataBlock req.
+ EC_WRITE_U32(data + 2, fsm->tx_packet_no); // PacketNo, Password
+
+ memcpy(data + EC_FOE_HEADER_SIZE,
+ fsm->tx_buffer + fsm->tx_buffer_offset, current_size);
+ fsm->tx_current_size = current_size;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Prepare a write request (WRQ) with filename
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_prepare_wrq_send(
+ ec_fsm_foe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ size_t current_size;
+ uint8_t *data;
+
+ fsm->tx_buffer_offset = 0;
+ fsm->tx_current_size = 0;
+ fsm->tx_packet_no = 0;
+ fsm->tx_last_packet = 0;
+
+ current_size = fsm->tx_filename_len;
+
+ data = ec_slave_mbox_prepare_send(fsm->slave, datagram,
+ EC_MBOX_TYPE_FILEACCESS, current_size + EC_FOE_HEADER_SIZE);
+ if (IS_ERR(data)) {
+ return -1;
+ }
+
+ EC_WRITE_U16( data, EC_FOE_OPCODE_WRQ); // fsm write request
+ EC_WRITE_U32( data + 2, fsm->tx_packet_no );
+
+ memcpy(data + EC_FOE_HEADER_SIZE, fsm->tx_filename, current_size);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Initializes the FoE write state machine.
+ */
+void ec_fsm_foe_write_start(
+ ec_fsm_foe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ fsm->tx_buffer_offset = 0;
+ fsm->tx_current_size = 0;
+ fsm->tx_packet_no = 0;
+ fsm->tx_last_packet = 0;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_FOE)) {
+ ec_foe_set_tx_error(fsm, FOE_MBOX_PROT_ERROR);
+ EC_SLAVE_ERR(slave, "Slave does not support FoE!\n");
+ return;
+ }
+
+ if (ec_foe_prepare_wrq_send(fsm, datagram)) {
+ ec_foe_set_tx_error(fsm, FOE_PROT_ERROR);
+ return;
+ }
+
+ fsm->state = ec_fsm_foe_state_wrq_sent;
+}
+
+/*****************************************************************************/
+
+/** Check for acknowledge.
+ */
+void ec_fsm_foe_state_ack_check(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to receive FoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE mailbox check datagram"
+ " failed: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(fsm->datagram)) {
+ // slave did not put anything in the mailbox yet
+ unsigned long diff_ms = (fsm->datagram->jiffies_received -
+ fsm->jiffies_start) * 1000 / HZ;
+ if (diff_ms >= EC_FSM_FOE_TIMEOUT) {
+ ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR);
+ EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n");
+ 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_foe_state_ack_read;
+}
+
+/*****************************************************************************/
+
+/** Acknowledge a read operation.
+ */
+void ec_fsm_foe_state_ack_read(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t *data, mbox_prot;
+ uint8_t opCode;
+ size_t rec_size;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to receive FoE ack response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE ack 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)) {
+ ec_foe_set_tx_error(fsm, FOE_PROT_ERROR);
+ return;
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_FILEACCESS) { // FoE
+ ec_foe_set_tx_error(fsm, FOE_MBOX_PROT_ERROR);
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ return;
+ }
+
+ opCode = EC_READ_U8(data);
+
+ if (opCode == EC_FOE_OPCODE_BUSY) {
+ // slave not ready
+ if (ec_foe_prepare_data_send(fsm, datagram)) {
+ ec_foe_set_tx_error(fsm, FOE_PROT_ERROR);
+ EC_SLAVE_ERR(slave, "Slave is busy.\n");
+ return;
+ }
+ fsm->state = ec_fsm_foe_state_data_sent;
+ return;
+ }
+
+ if (opCode == EC_FOE_OPCODE_ACK) {
+ fsm->tx_packet_no++;
+ fsm->tx_buffer_offset += fsm->tx_current_size;
+
+ if (fsm->tx_last_packet) {
+ fsm->state = ec_fsm_foe_end;
+ return;
+ }
+
+ if (ec_foe_prepare_data_send(fsm, datagram)) {
+ ec_foe_set_tx_error(fsm, FOE_PROT_ERROR);
+ return;
+ }
+ fsm->state = ec_fsm_foe_state_data_sent;
+ return;
+ }
+ ec_foe_set_tx_error(fsm, FOE_ACK_ERROR);
+}
+
+/*****************************************************************************/
+
+/** State: WRQ SENT.
+ *
+ * Checks is the previous transmit datagram succeded and sends the next
+ * fragment, if necessary.
+ */
+void ec_fsm_foe_state_wrq_sent(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to send FoE WRQ: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ // slave did not put anything in the mailbox yet
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE WRQ failed: ");
+ 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_foe_state_ack_check;
+}
+
+/*****************************************************************************/
+
+/** State: WRQ SENT.
+ *
+ * Checks is the previous transmit datagram succeded and sends the next
+ * fragment, if necessary.
+ */
+void ec_fsm_foe_state_data_sent(
+ ec_fsm_foe_t *fsm, /**< Foe statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_tx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to receive FoE ack response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ ec_foe_set_tx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE data send failed: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram);
+ fsm->jiffies_start = jiffies;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_foe_state_ack_check;
+}
+
+/*****************************************************************************/
+
+/** Prepare a read request (RRQ) with filename
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_prepare_rrq_send(
+ ec_fsm_foe_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ size_t current_size;
+ uint8_t *data;
+
+ current_size = fsm->rx_filename_len;
+
+ data = ec_slave_mbox_prepare_send(fsm->slave, datagram,
+ EC_MBOX_TYPE_FILEACCESS, current_size + EC_FOE_HEADER_SIZE);
+ if (IS_ERR(data)) {
+ return -1;
+ }
+
+ EC_WRITE_U16(data, EC_FOE_OPCODE_RRQ); // fsm read request
+ EC_WRITE_U32(data + 2, 0x00000000); // no passwd
+ memcpy(data + EC_FOE_HEADER_SIZE, fsm->rx_filename, current_size);
+
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(fsm->slave, 1, "FoE Read Request:\n");
+ ec_print_data(data, current_size + EC_FOE_HEADER_SIZE);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Prepare to send an acknowledge.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_foe_prepare_send_ack(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ uint8_t *data;
+
+ data = ec_slave_mbox_prepare_send(fsm->slave, datagram,
+ EC_MBOX_TYPE_FILEACCESS, EC_FOE_HEADER_SIZE);
+ if (IS_ERR(data)) {
+ return -1;
+ }
+
+ EC_WRITE_U16(data, EC_FOE_OPCODE_ACK);
+ EC_WRITE_U32(data + 2, fsm->rx_expected_packet_no);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** State: RRQ SENT.
+ *
+ * Checks is the previous transmit datagram succeeded and sends the next
+ * fragment, if necessary.
+ */
+void ec_fsm_foe_state_rrq_sent(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to send FoE RRQ: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ // slave did not put anything in the mailbox yet
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE RRQ failed: ");
+ 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_foe_state_data_check;
+}
+
+/*****************************************************************************/
+
+/** Starting state for read operations.
+ */
+void ec_fsm_foe_read_start(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ fsm->rx_buffer_offset = 0;
+ fsm->rx_expected_packet_no = 1;
+ fsm->rx_last_packet = 0;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_FOE)) {
+ ec_foe_set_tx_error(fsm, FOE_MBOX_PROT_ERROR);
+ EC_SLAVE_ERR(slave, "Slave does not support FoE!\n");
+ return;
+ }
+
+ if (ec_foe_prepare_rrq_send(fsm, datagram)) {
+ ec_foe_set_rx_error(fsm, FOE_PROT_ERROR);
+ return;
+ }
+
+ fsm->state = ec_fsm_foe_state_rrq_sent;
+}
+
+/*****************************************************************************/
+
+/** Check for data.
+ */
+void ec_fsm_foe_state_data_check(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to send FoE DATA READ: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE DATA READ: ");
+ 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_FSM_FOE_TIMEOUT) {
+ ec_foe_set_tx_error(fsm, FOE_TIMEOUT_ERROR);
+ EC_SLAVE_ERR(slave, "Timeout while waiting for ack response.\n");
+ 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_foe_state_data_read;
+}
+
+/*****************************************************************************/
+
+/** Start reading data.
+ */
+void ec_fsm_foe_state_data_read(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ size_t rec_size;
+ uint8_t *data, opCode, packet_no, mbox_prot;
+
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to receive FoE DATA READ datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE DATA READ 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)) {
+ ec_foe_set_rx_error(fsm, FOE_MBOX_FETCH_ERROR);
+ return;
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_FILEACCESS) { // FoE
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ ec_foe_set_rx_error(fsm, FOE_PROT_ERROR);
+ return;
+ }
+
+ opCode = EC_READ_U8(data);
+
+ if (opCode == EC_FOE_OPCODE_BUSY) {
+ if (ec_foe_prepare_send_ack(fsm, datagram)) {
+ ec_foe_set_rx_error(fsm, FOE_PROT_ERROR);
+ }
+ return;
+ }
+
+ if (opCode == EC_FOE_OPCODE_ERR) {
+ fsm->request->error_code = EC_READ_U32(data + 2);
+ EC_SLAVE_ERR(slave, "Received FoE Error Request (code 0x%08x).\n",
+ fsm->request->error_code);
+ if (rec_size > 6) {
+ uint8_t text[256];
+ strncpy(text, data + 6, min(rec_size - 6, sizeof(text)));
+ EC_SLAVE_ERR(slave, "FoE Error Text: %s\n", text);
+ }
+ ec_foe_set_rx_error(fsm, FOE_OPCODE_ERROR);
+ return;
+ }
+
+ if (opCode != EC_FOE_OPCODE_DATA) {
+ EC_SLAVE_ERR(slave, "Received OPCODE %x, expected %x.\n",
+ opCode, EC_FOE_OPCODE_DATA);
+ fsm->request->error_code = 0x00000000;
+ ec_foe_set_rx_error(fsm, FOE_OPCODE_ERROR);
+ return;
+ }
+
+ packet_no = EC_READ_U16(data + 2);
+ if (packet_no != fsm->rx_expected_packet_no) {
+ EC_SLAVE_ERR(slave, "Received unexpected packet number.\n");
+ ec_foe_set_rx_error(fsm, FOE_PACKETNO_ERROR);
+ return;
+ }
+
+ rec_size -= EC_FOE_HEADER_SIZE;
+
+ if (fsm->rx_buffer_size >= fsm->rx_buffer_offset + rec_size) {
+ memcpy(fsm->rx_buffer + fsm->rx_buffer_offset,
+ data + EC_FOE_HEADER_SIZE, rec_size);
+ fsm->rx_buffer_offset += rec_size;
+ }
+
+ fsm->rx_last_packet =
+ (rec_size + EC_MBOX_HEADER_SIZE + EC_FOE_HEADER_SIZE
+ != slave->configured_rx_mailbox_size);
+
+ if (fsm->rx_last_packet ||
+ (slave->configured_rx_mailbox_size - EC_MBOX_HEADER_SIZE
+ - EC_FOE_HEADER_SIZE + fsm->rx_buffer_offset)
+ <= fsm->rx_buffer_size) {
+ // either it was the last packet or a new packet will fit into the
+ // delivered buffer
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "last_packet=true\n");
+#endif
+ if (ec_foe_prepare_send_ack(fsm, datagram)) {
+ ec_foe_set_rx_error(fsm, FOE_RX_DATA_ACK_ERROR);
+ return;
+ }
+
+ fsm->state = ec_fsm_foe_state_sent_ack;
+ }
+ else {
+ // no more data fits into the delivered buffer
+ // ... wait for new read request
+ EC_SLAVE_ERR(slave, "Data do not fit in receive buffer!\n");
+ printk(" rx_buffer_size = %d\n", fsm->rx_buffer_size);
+ printk("rx_buffer_offset = %d\n", fsm->rx_buffer_offset);
+ printk(" rec_size = %zd\n", rec_size);
+ printk(" rx_mailbox_size = %d\n", slave->configured_rx_mailbox_size);
+ printk(" rx_last_packet = %d\n", fsm->rx_last_packet);
+ fsm->request->result = FOE_READY;
+ }
+}
+
+/*****************************************************************************/
+
+/** Sent an acknowledge.
+ */
+void ec_fsm_foe_state_sent_ack(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+#ifdef DEBUG_FOE
+ EC_SLAVE_DBG(fsm->slave, 0, "%s()\n", __func__);
+#endif
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ ec_foe_set_rx_error(fsm, FOE_RECEIVE_ERROR);
+ EC_SLAVE_ERR(slave, "Failed to send FoE ACK: ");
+ ec_datagram_print_state(fsm->datagram);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ // slave did not put anything into the mailbox yet
+ ec_foe_set_rx_error(fsm, FOE_WC_ERROR);
+ EC_SLAVE_ERR(slave, "Reception of FoE ACK failed: ");
+ 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.
+
+ if (fsm->rx_last_packet) {
+ fsm->rx_expected_packet_no = 0;
+ fsm->request->data_size = fsm->rx_buffer_offset;
+ fsm->state = ec_fsm_foe_end;
+ }
+ else {
+ fsm->rx_expected_packet_no++;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_foe_state_data_check;
+ }
+}
+
+/*****************************************************************************/
+
+/** Set an error code and go to the send error state.
+ */
+void ec_foe_set_tx_error(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ uint32_t errorcode /**< FoE error code. */
+ )
+{
+ fsm->request->result = errorcode;
+ fsm->state = ec_fsm_foe_error;
+}
+
+/*****************************************************************************/
+
+/** Set an error code and go to the receive error state.
+ */
+void ec_foe_set_rx_error(
+ ec_fsm_foe_t *fsm, /**< FoE statemachine. */
+ uint32_t errorcode /**< FoE error code. */
+ )
+{
+ fsm->request->result = errorcode;
+ fsm->state = ec_fsm_foe_error;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_foe.h b/net/ethercat/master/fsm_foe.h
new file mode 100644
index 000000000000..91d6df548ca5
--- /dev/null
+++ b/net/ethercat/master/fsm_foe.h
@@ -0,0 +1,94 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2008 Olav Zarges, imc Messsysteme GmbH
+ * 2009-2012 Florian Pose <fp@igh-essen.com>
+ *
+ * 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 FoE state machines.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_FOE_H__
+#define __EC_FSM_FOE_H__
+
+#include "globals.h"
+#include "../include/ecrt.h"
+#include "datagram.h"
+#include "slave.h"
+#include "foe_request.h"
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_foe ec_fsm_foe_t; /**< \see ec_fsm_foe */
+
+/** Finite state machines for the CANopen-over-EtherCAT protocol.
+ */
+struct ec_fsm_foe {
+ ec_slave_t *slave; /**< Slave the FSM runs on. */
+ unsigned int retries; /**< Retries upon datagram timeout */
+
+ void (*state)(ec_fsm_foe_t *, ec_datagram_t *); /**< FoE state function. */
+ ec_datagram_t *datagram; /**< Datagram used in previous step. */
+ unsigned long jiffies_start; /**< FoE timestamp. */
+ uint8_t subindex; /**< Current subindex. */
+ ec_foe_request_t *request; /**< FoE request. */
+ uint8_t toggle; /**< Toggle bit for segment commands. */
+
+ uint8_t *tx_buffer; /**< Buffer with data to transmit. */
+ uint32_t tx_buffer_size; /**< Size of data to transmit. */
+ uint32_t tx_buffer_offset; /**< Offset of data to tranmit next. */
+ uint32_t tx_last_packet; /**< Current packet is last one to send. */
+ uint32_t tx_packet_no; /**< FoE packet number. */
+ uint32_t tx_current_size; /**< Size of current packet to send. */
+ uint8_t *tx_filename; /**< Name of file to transmit. */
+ uint32_t tx_filename_len; /**< Lenth of transmit file name. */
+
+ uint8_t *rx_buffer; /**< Buffer for received data. */
+ uint32_t rx_buffer_size; /**< Size of receive buffer. */
+ uint32_t rx_buffer_offset; /**< Offset in receive buffer. */
+ uint32_t rx_expected_packet_no; /**< Expected receive packet number. */
+ uint32_t rx_last_packet; /**< Current packet is the last to receive. */
+ uint8_t *rx_filename; /**< Name of the file to receive. */
+ uint32_t rx_filename_len; /**< Length of the receive file name. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_foe_init(ec_fsm_foe_t *);
+void ec_fsm_foe_clear(ec_fsm_foe_t *);
+
+int ec_fsm_foe_exec(ec_fsm_foe_t *, ec_datagram_t *);
+int ec_fsm_foe_success(const ec_fsm_foe_t *);
+
+void ec_fsm_foe_transfer(ec_fsm_foe_t *, ec_slave_t *, ec_foe_request_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_master.c b/net/ethercat/master/fsm_master.c
new file mode 100644
index 000000000000..719e91bfa02b
--- /dev/null
+++ b/net/ethercat/master/fsm_master.c
@@ -0,0 +1,1306 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master state machine.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+#ifdef EC_EOE
+#include "ethernet.h"
+#endif
+
+#include "fsm_master.h"
+#include "fsm_foe.h"
+
+/*****************************************************************************/
+
+/** Time difference [ns] to tolerate without setting a new system time offset.
+ */
+#define EC_SYSTEM_TIME_TOLERANCE_NS 1000000
+
+/*****************************************************************************/
+
+void ec_fsm_master_state_start(ec_fsm_master_t *);
+void ec_fsm_master_state_broadcast(ec_fsm_master_t *);
+void ec_fsm_master_state_read_state(ec_fsm_master_t *);
+void ec_fsm_master_state_acknowledge(ec_fsm_master_t *);
+void ec_fsm_master_state_configure_slave(ec_fsm_master_t *);
+void ec_fsm_master_state_clear_addresses(ec_fsm_master_t *);
+void ec_fsm_master_state_dc_measure_delays(ec_fsm_master_t *);
+void ec_fsm_master_state_scan_slave(ec_fsm_master_t *);
+void ec_fsm_master_state_dc_read_offset(ec_fsm_master_t *);
+void ec_fsm_master_state_dc_write_offset(ec_fsm_master_t *);
+void ec_fsm_master_state_write_sii(ec_fsm_master_t *);
+void ec_fsm_master_state_sdo_dictionary(ec_fsm_master_t *);
+void ec_fsm_master_state_sdo_request(ec_fsm_master_t *);
+
+void ec_fsm_master_enter_clear_addresses(ec_fsm_master_t *);
+void ec_fsm_master_enter_write_system_times(ec_fsm_master_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_master_init(
+ ec_fsm_master_t *fsm, /**< Master state machine. */
+ ec_master_t *master, /**< EtherCAT master. */
+ ec_datagram_t *datagram /**< Datagram object to use. */
+ )
+{
+ fsm->master = master;
+ fsm->datagram = datagram;
+
+ ec_fsm_master_reset(fsm);
+
+ // init sub-state-machines
+ ec_fsm_coe_init(&fsm->fsm_coe);
+ ec_fsm_soe_init(&fsm->fsm_soe);
+ ec_fsm_pdo_init(&fsm->fsm_pdo, &fsm->fsm_coe);
+ ec_fsm_change_init(&fsm->fsm_change, fsm->datagram);
+ ec_fsm_slave_config_init(&fsm->fsm_slave_config, fsm->datagram,
+ &fsm->fsm_change, &fsm->fsm_coe, &fsm->fsm_soe, &fsm->fsm_pdo);
+ ec_fsm_slave_scan_init(&fsm->fsm_slave_scan, fsm->datagram,
+ &fsm->fsm_slave_config, &fsm->fsm_pdo);
+ ec_fsm_sii_init(&fsm->fsm_sii, fsm->datagram);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_master_clear(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ // clear sub-state machines
+ ec_fsm_coe_clear(&fsm->fsm_coe);
+ ec_fsm_soe_clear(&fsm->fsm_soe);
+ ec_fsm_pdo_clear(&fsm->fsm_pdo);
+ ec_fsm_change_clear(&fsm->fsm_change);
+ ec_fsm_slave_config_clear(&fsm->fsm_slave_config);
+ ec_fsm_slave_scan_clear(&fsm->fsm_slave_scan);
+ ec_fsm_sii_clear(&fsm->fsm_sii);
+}
+
+/*****************************************************************************/
+
+/** Reset state machine.
+ */
+void ec_fsm_master_reset(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_device_index_t dev_idx;
+
+ fsm->state = ec_fsm_master_state_start;
+ fsm->idle = 0;
+ fsm->dev_idx = EC_DEVICE_MAIN;
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(fsm->master); dev_idx++) {
+ fsm->link_state[dev_idx] = 0;
+ fsm->slaves_responding[dev_idx] = 0;
+ fsm->slave_states[dev_idx] = EC_SLAVE_STATE_UNKNOWN;
+ }
+
+ fsm->rescan_required = 0;
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * If the state machine's datagram is not sent or received yet, the execution
+ * of the state machine is delayed to the next cycle.
+ *
+ * \return true, if the state machine was executed
+ */
+int ec_fsm_master_exec(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ if (fsm->datagram->state == EC_DATAGRAM_SENT
+ || fsm->datagram->state == EC_DATAGRAM_QUEUED) {
+ // datagram was not sent or received yet.
+ return 0;
+ }
+
+ fsm->state(fsm);
+ return 1;
+}
+
+/*****************************************************************************/
+
+/**
+ * \return true, if the state machine is in an idle phase
+ */
+int ec_fsm_master_idle(
+ const ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ return fsm->idle;
+}
+
+/*****************************************************************************/
+
+/** Restarts the master state machine.
+ */
+void ec_fsm_master_restart(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ fsm->dev_idx = EC_DEVICE_MAIN;
+ fsm->state = ec_fsm_master_state_start;
+ fsm->state(fsm); // execute immediately
+}
+
+/******************************************************************************
+ * Master state machine
+ *****************************************************************************/
+
+/** Master state: START.
+ *
+ * Starts with getting slave count and slave states.
+ */
+void ec_fsm_master_state_start(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+
+ fsm->idle = 1;
+
+ // check for emergency requests
+ if (!list_empty(&master->emerg_reg_requests)) {
+ ec_reg_request_t *request;
+
+ // get first request
+ request = list_entry(master->emerg_reg_requests.next,
+ ec_reg_request_t, list);
+ list_del_init(&request->list); // dequeue
+ request->state = EC_INT_REQUEST_BUSY;
+
+ if (request->transfer_size > fsm->datagram->mem_size) {
+ EC_MASTER_ERR(master, "Emergency request data too large!\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&master->request_queue);
+ fsm->state(fsm); // continue
+ return;
+ }
+
+ if (request->dir != EC_DIR_OUTPUT) {
+ EC_MASTER_ERR(master, "Emergency requests must be"
+ " write requests!\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&master->request_queue);
+ fsm->state(fsm); // continue
+ return;
+ }
+
+ EC_MASTER_DBG(master, 1, "Writing emergency register request...\n");
+ ec_datagram_apwr(fsm->datagram, request->ring_position,
+ request->address, request->transfer_size);
+ memcpy(fsm->datagram->data, request->data, request->transfer_size);
+ fsm->datagram->device_index = EC_DEVICE_MAIN;
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&master->request_queue);
+ return;
+ }
+
+ ec_datagram_brd(fsm->datagram, 0x0130, 2);
+ ec_datagram_zero(fsm->datagram);
+ fsm->datagram->device_index = fsm->dev_idx;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_broadcast;
+}
+
+/*****************************************************************************/
+
+/** Master state: BROADCAST.
+ *
+ * Processes the broadcast read slave count and slaves states.
+ */
+void ec_fsm_master_state_broadcast(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ unsigned int i, size;
+ ec_slave_t *slave;
+ ec_master_t *master = fsm->master;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ // bus topology change?
+ if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
+ fsm->rescan_required = 1;
+ fsm->slaves_responding[fsm->dev_idx] = datagram->working_counter;
+ EC_MASTER_INFO(master, "%u slave(s) responding on %s device.\n",
+ fsm->slaves_responding[fsm->dev_idx],
+ ec_device_names[fsm->dev_idx != 0]);
+ }
+
+ if (fsm->link_state[fsm->dev_idx] &&
+ !master->devices[fsm->dev_idx].link_state) {
+ ec_device_index_t dev_idx;
+
+ EC_MASTER_DBG(master, 1, "Master state machine detected "
+ "link down on %s device. Clearing slave list.\n",
+ ec_device_names[fsm->dev_idx != 0]);
+
+#ifdef EC_EOE
+ ec_master_eoe_stop(master);
+ ec_master_clear_eoe_handlers(master);
+#endif
+ ec_master_clear_slaves(master);
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(master); dev_idx++) {
+ fsm->slave_states[dev_idx] = 0x00;
+ fsm->slaves_responding[dev_idx] = 0; /* Reset to trigger rescan on
+ next link up. */
+ }
+ }
+ fsm->link_state[fsm->dev_idx] = master->devices[fsm->dev_idx].link_state;
+
+ if (datagram->state == EC_DATAGRAM_RECEIVED &&
+ fsm->slaves_responding[fsm->dev_idx]) {
+ uint8_t states = EC_READ_U8(datagram->data);
+ if (states != fsm->slave_states[fsm->dev_idx]) {
+ // slave states changed
+ char state_str[EC_STATE_STRING_SIZE];
+ fsm->slave_states[fsm->dev_idx] = states;
+ ec_state_string(states, state_str, 1);
+ EC_MASTER_INFO(master, "Slave states on %s device: %s.\n",
+ ec_device_names[fsm->dev_idx != 0], state_str);
+ }
+ } else {
+ fsm->slave_states[fsm->dev_idx] = 0x00;
+ }
+
+ fsm->dev_idx++;
+ if (fsm->dev_idx < ec_master_num_devices(master)) {
+ // check number of responding slaves on next device
+ fsm->state = ec_fsm_master_state_start;
+ fsm->state(fsm); // execute immediately
+ return;
+ }
+
+ if (fsm->rescan_required) {
+ down(&master->scan_sem);
+ if (!master->allow_scan) {
+ up(&master->scan_sem);
+ } else {
+ unsigned int count = 0, next_dev_slave, ring_position;
+ ec_device_index_t dev_idx;
+
+ master->scan_busy = 1;
+ up(&master->scan_sem);
+
+ // clear all slaves and scan the bus
+ fsm->rescan_required = 0;
+ fsm->idle = 0;
+ fsm->scan_jiffies = jiffies;
+
+#ifdef EC_EOE
+ ec_master_eoe_stop(master);
+ ec_master_clear_eoe_handlers(master);
+#endif
+ ec_master_clear_slaves(master);
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(master); dev_idx++) {
+ count += fsm->slaves_responding[dev_idx];
+ }
+
+ if (!count) {
+ // no slaves present -> finish state machine.
+ master->scan_busy = 0;
+ wake_up_interruptible(&master->scan_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ size = sizeof(ec_slave_t) * count;
+ if (!(master->slaves =
+ (ec_slave_t *) kmalloc(size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %u bytes"
+ " of slave memory!\n", size);
+ master->scan_busy = 0;
+ wake_up_interruptible(&master->scan_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ // init slaves
+ dev_idx = EC_DEVICE_MAIN;
+ next_dev_slave = fsm->slaves_responding[dev_idx];
+ ring_position = 0;
+ for (i = 0; i < count; i++, ring_position++) {
+ slave = master->slaves + i;
+ while (i >= next_dev_slave) {
+ dev_idx++;
+ next_dev_slave += fsm->slaves_responding[dev_idx];
+ ring_position = 0;
+ }
+
+ ec_slave_init(slave, master, dev_idx, ring_position, i + 1);
+
+ // do not force reconfiguration in operation phase to avoid
+ // unnecesssary process data interruptions
+ if (master->phase != EC_OPERATION) {
+ slave->force_config = 1;
+ }
+ }
+ master->slave_count = count;
+ master->fsm_slave = master->slaves;
+
+ /* start with first device with slaves responding; at least one
+ * has responding slaves, otherwise count would be zero. */
+ fsm->dev_idx = EC_DEVICE_MAIN;
+ while (!fsm->slaves_responding[fsm->dev_idx]) {
+ fsm->dev_idx++;
+ }
+
+ ec_fsm_master_enter_clear_addresses(fsm);
+ return;
+ }
+ }
+
+ if (master->slave_count) {
+
+ // application applied configurations
+ if (master->config_changed) {
+ master->config_changed = 0;
+
+ EC_MASTER_DBG(master, 1, "Configuration changed.\n");
+
+ fsm->slave = master->slaves; // begin with first slave
+ ec_fsm_master_enter_write_system_times(fsm);
+
+ } else {
+ // fetch state from first slave
+ fsm->slave = master->slaves;
+ ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
+ 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->datagram->device_index = fsm->slave->device_index;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_read_state;
+ }
+ } else {
+ ec_fsm_master_restart(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Check for pending SII write requests and process one.
+ *
+ * \return non-zero, if an SII write request is processed.
+ */
+int ec_fsm_master_action_process_sii(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_sii_write_request_t *request;
+
+ // search the first request to be processed
+ while (1) {
+ if (list_empty(&master->sii_requests))
+ break;
+
+ // get first request
+ request = list_entry(master->sii_requests.next,
+ ec_sii_write_request_t, list);
+ list_del_init(&request->list); // dequeue
+ request->state = EC_INT_REQUEST_BUSY;
+
+ // found pending SII write operation. execute it!
+ EC_SLAVE_DBG(request->slave, 1, "Writing SII data...\n");
+ fsm->sii_request = request;
+ fsm->sii_index = 0;
+ ec_fsm_sii_write(&fsm->fsm_sii, request->slave, request->offset,
+ request->words, EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ fsm->state = ec_fsm_master_state_write_sii;
+ fsm->state(fsm); // execute immediately
+ return 1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Check for pending SDO requests and process one.
+ *
+ * \return non-zero, if an SDO request is processed.
+ */
+int ec_fsm_master_action_process_sdo(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave;
+ ec_sdo_request_t *req;
+
+ // search for internal requests to be processed
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+
+ if (!slave->config) {
+ continue;
+ }
+
+ list_for_each_entry(req, &slave->config->sdo_requests, list) {
+ if (req->state == EC_INT_REQUEST_QUEUED) {
+
+ if (ec_sdo_request_timed_out(req)) {
+ req->state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_DBG(slave, 1, "Internal SDO request"
+ " timed out.\n");
+ continue;
+ }
+
+ if (slave->current_state == EC_SLAVE_STATE_INIT) {
+ req->state = EC_INT_REQUEST_FAILURE;
+ continue;
+ }
+
+ req->state = EC_INT_REQUEST_BUSY;
+ EC_SLAVE_DBG(slave, 1, "Processing internal"
+ " SDO request...\n");
+ fsm->idle = 0;
+ fsm->sdo_request = req;
+ fsm->slave = slave;
+ fsm->state = ec_fsm_master_state_sdo_request;
+ ec_fsm_coe_transfer(&fsm->fsm_coe, slave, req);
+ ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram);
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Master action: IDLE.
+ *
+ * Does secondary work.
+ */
+void ec_fsm_master_action_idle(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave;
+
+ // Check for pending internal SDO requests
+ if (ec_fsm_master_action_process_sdo(fsm)) {
+ return;
+ }
+
+ // enable processing of requests
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ ec_fsm_slave_set_ready(&slave->fsm);
+ }
+
+ // check, if slaves have an SDO dictionary to read out.
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_COE)
+ || (slave->sii.has_general
+ && !slave->sii.coe_details.enable_sdo_info)
+ || slave->sdo_dictionary_fetched
+ || slave->current_state == EC_SLAVE_STATE_INIT
+ || slave->current_state == EC_SLAVE_STATE_UNKNOWN
+ || jiffies - slave->jiffies_preop < EC_WAIT_SDO_DICT * HZ
+ ) continue;
+
+ EC_SLAVE_DBG(slave, 1, "Fetching SDO dictionary.\n");
+
+ slave->sdo_dictionary_fetched = 1;
+
+ // start fetching SDO dictionary
+ fsm->idle = 0;
+ fsm->slave = slave;
+ fsm->state = ec_fsm_master_state_sdo_dictionary;
+ ec_fsm_coe_dictionary(&fsm->fsm_coe, slave);
+ ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
+ return;
+ }
+
+ // check for pending SII write operations.
+ if (ec_fsm_master_action_process_sii(fsm)) {
+ return; // SII write request found
+ }
+
+ ec_fsm_master_restart(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master action: Get state of next slave.
+ */
+void ec_fsm_master_action_next_slave_state(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+
+ // is there another slave to query?
+ fsm->slave++;
+ if (fsm->slave < master->slaves + master->slave_count) {
+ // fetch state from next slave
+ fsm->idle = 1;
+ ec_datagram_fprd(fsm->datagram,
+ fsm->slave->station_address, 0x0130, 2);
+ ec_datagram_zero(fsm->datagram);
+ fsm->datagram->device_index = fsm->slave->device_index;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_read_state;
+ return;
+ }
+
+ // all slaves processed
+ ec_fsm_master_action_idle(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master action: Configure.
+ */
+void ec_fsm_master_action_configure(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_slave_t *slave = fsm->slave;
+
+ if (master->config_changed) {
+ master->config_changed = 0;
+
+ // abort iterating through slaves,
+ // first compensate DC system time offsets,
+ // then begin configuring at slave 0
+ EC_MASTER_DBG(master, 1, "Configuration changed"
+ " (aborting state check).\n");
+
+ fsm->slave = master->slaves; // begin with first slave
+ ec_fsm_master_enter_write_system_times(fsm);
+ return;
+ }
+
+ // Does the slave have to be configured?
+ if ((slave->current_state != slave->requested_state
+ || slave->force_config) && !slave->error_flag) {
+
+ // Start slave configuration
+ down(&master->config_sem);
+ master->config_busy = 1;
+ up(&master->config_sem);
+
+ if (master->debug_level) {
+ char old_state[EC_STATE_STRING_SIZE],
+ new_state[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, old_state, 0);
+ ec_state_string(slave->requested_state, new_state, 0);
+ EC_SLAVE_DBG(slave, 1, "Changing state from %s to %s%s.\n",
+ old_state, new_state,
+ slave->force_config ? " (forced)" : "");
+ }
+
+ fsm->idle = 0;
+ fsm->state = ec_fsm_master_state_configure_slave;
+ ec_fsm_slave_config_start(&fsm->fsm_slave_config, slave);
+ fsm->state(fsm); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
+ return;
+ }
+
+ // process next slave
+ ec_fsm_master_action_next_slave_state(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: READ STATE.
+ *
+ * Fetches the AL state of a slave.
+ */
+void ec_fsm_master_state_read_state(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_ERR(slave, "Failed to receive AL state datagram: ");
+ ec_datagram_print_state(datagram);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ // did the slave not respond to its station address?
+ if (datagram->working_counter != 1) {
+ if (!slave->error_flag) {
+ slave->error_flag = 1;
+ EC_SLAVE_DBG(slave, 1, "Slave did not respond to state query.\n");
+ }
+ fsm->rescan_required = 1;
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ // A single slave responded
+ ec_slave_set_state(slave, EC_READ_U8(datagram->data));
+
+ if (!slave->error_flag) {
+ // Check, if new slave state has to be acknowledged
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ fsm->idle = 0;
+ fsm->state = ec_fsm_master_state_acknowledge;
+ ec_fsm_change_ack(&fsm->fsm_change, slave);
+ fsm->state(fsm); // execute immediately
+ return;
+ }
+
+ // No acknowlegde necessary; check for configuration
+ ec_fsm_master_action_configure(fsm);
+ return;
+ }
+
+ // slave has error flag set; process next one
+ ec_fsm_master_action_next_slave_state(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: ACKNOWLEDGE.
+ */
+void ec_fsm_master_state_acknowledge(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(&fsm->fsm_change)) {
+ return;
+ }
+
+ if (!ec_fsm_change_success(&fsm->fsm_change)) {
+ fsm->slave->error_flag = 1;
+ EC_SLAVE_ERR(slave, "Failed to acknowledge state change.\n");
+ }
+
+ ec_fsm_master_action_configure(fsm);
+}
+
+/*****************************************************************************/
+
+/** Start clearing slave addresses.
+ */
+void ec_fsm_master_enter_clear_addresses(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ // broadcast clear all station addresses
+ ec_datagram_bwr(fsm->datagram, 0x0010, 2);
+ EC_WRITE_U16(fsm->datagram->data, 0x0000);
+ fsm->datagram->device_index = fsm->dev_idx;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_clear_addresses;
+}
+
+/*****************************************************************************/
+
+/** Master state: CLEAR ADDRESSES.
+ */
+void ec_fsm_master_state_clear_addresses(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_MASTER_ERR(master, "Failed to receive address"
+ " clearing datagram on %s link: ",
+ ec_device_names[fsm->dev_idx != 0]);
+ ec_datagram_print_state(datagram);
+ master->scan_busy = 0;
+ wake_up_interruptible(&master->scan_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ if (datagram->working_counter != fsm->slaves_responding[fsm->dev_idx]) {
+ EC_MASTER_WARN(master, "Failed to clear station addresses on %s link:"
+ " Cleared %u of %u",
+ ec_device_names[fsm->dev_idx != 0], datagram->working_counter,
+ fsm->slaves_responding[fsm->dev_idx]);
+ }
+
+ EC_MASTER_DBG(master, 1, "Sending broadcast-write"
+ " to measure transmission delays on %s link.\n",
+ ec_device_names[fsm->dev_idx != 0]);
+
+ ec_datagram_bwr(datagram, 0x0900, 1);
+ ec_datagram_zero(datagram);
+ fsm->datagram->device_index = fsm->dev_idx;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_dc_measure_delays;
+}
+
+/*****************************************************************************/
+
+/** Master state: DC MEASURE DELAYS.
+ */
+void ec_fsm_master_state_dc_measure_delays(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_MASTER_ERR(master, "Failed to receive delay measuring datagram"
+ " on %s link: ", ec_device_names[fsm->dev_idx != 0]);
+ ec_datagram_print_state(datagram);
+ master->scan_busy = 0;
+ wake_up_interruptible(&master->scan_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ EC_MASTER_DBG(master, 1, "%u slaves responded to delay measuring"
+ " on %s link.\n",
+ datagram->working_counter, ec_device_names[fsm->dev_idx != 0]);
+
+ do {
+ fsm->dev_idx++;
+ } while (fsm->dev_idx < ec_master_num_devices(master) &&
+ !fsm->slaves_responding[fsm->dev_idx]);
+ if (fsm->dev_idx < ec_master_num_devices(master)) {
+ ec_fsm_master_enter_clear_addresses(fsm);
+ return;
+ }
+
+ EC_MASTER_INFO(master, "Scanning bus.\n");
+
+ // begin scanning of slaves
+ fsm->slave = master->slaves;
+ EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+ fsm->slave->ring_position,
+ ec_device_names[fsm->slave->device_index != 0]);
+ fsm->state = ec_fsm_master_state_scan_slave;
+ ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
+ ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
+}
+
+/*****************************************************************************/
+
+/** Master state: SCAN SLAVE.
+ *
+ * Executes the sub-statemachine for the scanning of a slave.
+ */
+void ec_fsm_master_state_scan_slave(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+#ifdef EC_EOE
+ ec_slave_t *slave = fsm->slave;
+#endif
+
+ if (ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan)) {
+ return;
+ }
+
+#ifdef EC_EOE
+ if (slave->sii.mailbox_protocols & EC_MBOX_EOE) {
+ // create EoE handler for this slave
+ ec_eoe_t *eoe;
+ if (!(eoe = kmalloc(sizeof(ec_eoe_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate EoE handler memory!\n");
+ } else if (ec_eoe_init(eoe, slave)) {
+ EC_SLAVE_ERR(slave, "Failed to init EoE handler!\n");
+ kfree(eoe);
+ } else {
+ list_add_tail(&eoe->list, &master->eoe_handlers);
+ }
+ }
+#endif
+
+ // another slave to fetch?
+ fsm->slave++;
+ if (fsm->slave < master->slaves + master->slave_count) {
+ EC_MASTER_DBG(master, 1, "Scanning slave %u on %s link.\n",
+ fsm->slave->ring_position,
+ ec_device_names[fsm->slave->device_index != 0]);
+ ec_fsm_slave_scan_start(&fsm->fsm_slave_scan, fsm->slave);
+ ec_fsm_slave_scan_exec(&fsm->fsm_slave_scan); // execute immediately
+ fsm->datagram->device_index = fsm->slave->device_index;
+ return;
+ }
+
+ EC_MASTER_INFO(master, "Bus scanning completed in %lu ms.\n",
+ (jiffies - fsm->scan_jiffies) * 1000 / HZ);
+
+ master->scan_busy = 0;
+ wake_up_interruptible(&master->scan_queue);
+
+ ec_master_calc_dc(master);
+
+ // Attach slave configurations
+ ec_master_attach_slave_configs(master);
+
+#ifdef EC_EOE
+ // check if EoE processing has to be started
+ ec_master_eoe_start(master);
+#endif
+
+ if (master->slave_count) {
+ master->config_changed = 0;
+
+ fsm->slave = master->slaves; // begin with first slave
+ ec_fsm_master_enter_write_system_times(fsm);
+ } else {
+ ec_fsm_master_restart(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Master state: CONFIGURE SLAVE.
+ *
+ * Starts configuring a slave.
+ */
+void ec_fsm_master_state_configure_slave(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+
+ if (ec_fsm_slave_config_exec(&fsm->fsm_slave_config)) {
+ return;
+ }
+
+ fsm->slave->force_config = 0;
+
+ // configuration finished
+ master->config_busy = 0;
+ wake_up_interruptible(&master->config_queue);
+
+ if (!ec_fsm_slave_config_success(&fsm->fsm_slave_config)) {
+ // TODO: mark slave_config as failed.
+ }
+
+ fsm->idle = 1;
+ ec_fsm_master_action_next_slave_state(fsm);
+}
+
+/*****************************************************************************/
+
+/** Start writing DC system times.
+ */
+void ec_fsm_master_enter_write_system_times(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+
+ if (master->dc_ref_time) {
+
+ while (fsm->slave < master->slaves + master->slave_count) {
+ if (!fsm->slave->base_dc_supported
+ || !fsm->slave->has_dc_system_time) {
+ fsm->slave++;
+ continue;
+ }
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Checking system time offset.\n");
+
+ // read DC system time (0x0910, 64 bit)
+ // gap (64 bit)
+ // and time offset (0x0920, 64 bit)
+ ec_datagram_fprd(fsm->datagram, fsm->slave->station_address,
+ 0x0910, 24);
+ fsm->datagram->device_index = fsm->slave->device_index;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_dc_read_offset;
+ return;
+ }
+
+ } else {
+ if (master->active) {
+ EC_MASTER_WARN(master, "No application time received up to now,"
+ " but master already active.\n");
+ } else {
+ EC_MASTER_DBG(master, 1, "No app_time received up to now.\n");
+ }
+ }
+
+ // scanning and setting system times complete
+ ec_master_request_op(master);
+ ec_fsm_master_restart(fsm);
+}
+
+/*****************************************************************************/
+
+/** Configure 32 bit time offset.
+ *
+ * \return New offset.
+ */
+u64 ec_fsm_master_dc_offset32(
+ ec_fsm_master_t *fsm, /**< Master state machine. */
+ u64 system_time, /**< System time register. */
+ u64 old_offset, /**< Time offset register. */
+ unsigned long jiffies_since_read /**< Jiffies for correction. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ u32 correction, system_time32, old_offset32, new_offset;
+ s32 time_diff;
+
+ system_time32 = (u32) system_time;
+ old_offset32 = (u32) old_offset;
+
+ // correct read system time by elapsed time since read operation
+ correction = jiffies_since_read * 1000 / HZ * 1000000;
+ system_time32 += correction;
+ time_diff = (u32) slave->master->app_time - system_time32;
+
+ EC_SLAVE_DBG(slave, 1, "DC 32 bit system time offset calculation:"
+ " system_time=%u (corrected with %u),"
+ " app_time=%llu, diff=%i\n",
+ system_time32, correction,
+ slave->master->app_time, time_diff);
+
+ if (EC_ABS(time_diff) > EC_SYSTEM_TIME_TOLERANCE_NS) {
+ new_offset = time_diff + old_offset32;
+ EC_SLAVE_DBG(slave, 1, "Setting time offset to %u (was %u)\n",
+ new_offset, old_offset32);
+ return (u64) new_offset;
+ } else {
+ EC_SLAVE_DBG(slave, 1, "Not touching time offset.\n");
+ return old_offset;
+ }
+}
+
+/*****************************************************************************/
+
+/** Configure 64 bit time offset.
+ *
+ * \return New offset.
+ */
+u64 ec_fsm_master_dc_offset64(
+ ec_fsm_master_t *fsm, /**< Master state machine. */
+ u64 system_time, /**< System time register. */
+ u64 old_offset, /**< Time offset register. */
+ unsigned long jiffies_since_read /**< Jiffies for correction. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ u64 new_offset, correction;
+ s64 time_diff;
+
+ // correct read system time by elapsed time since read operation
+ correction = (u64) (jiffies_since_read * 1000 / HZ) * 1000000;
+ system_time += correction;
+ time_diff = fsm->slave->master->app_time - system_time;
+
+ EC_SLAVE_DBG(slave, 1, "DC 64 bit system time offset calculation:"
+ " system_time=%llu (corrected with %llu),"
+ " app_time=%llu, diff=%lli\n",
+ system_time, correction,
+ slave->master->app_time, time_diff);
+
+ if (EC_ABS(time_diff) > EC_SYSTEM_TIME_TOLERANCE_NS) {
+ new_offset = time_diff + old_offset;
+ EC_SLAVE_DBG(slave, 1, "Setting time offset to %llu (was %llu)\n",
+ new_offset, old_offset);
+ } else {
+ new_offset = old_offset;
+ EC_SLAVE_DBG(slave, 1, "Not touching time offset.\n");
+ }
+
+ return new_offset;
+}
+
+/*****************************************************************************/
+
+/** Master state: DC READ OFFSET.
+ */
+void ec_fsm_master_state_dc_read_offset(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ u64 system_time, old_offset, new_offset;
+ unsigned long jiffies_since_read;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_ERR(slave, "Failed to receive DC times datagram: ");
+ ec_datagram_print_state(datagram);
+ fsm->slave++;
+ ec_fsm_master_enter_write_system_times(fsm);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(slave, "Failed to get DC times: ");
+ ec_datagram_print_wc_error(datagram);
+ fsm->slave++;
+ ec_fsm_master_enter_write_system_times(fsm);
+ return;
+ }
+
+ system_time = EC_READ_U64(datagram->data); // 0x0910
+ old_offset = EC_READ_U64(datagram->data + 16); // 0x0920
+ jiffies_since_read = jiffies - datagram->jiffies_sent;
+
+ if (slave->base_dc_range == EC_DC_32) {
+ new_offset = ec_fsm_master_dc_offset32(fsm,
+ system_time, old_offset, jiffies_since_read);
+ } else {
+ new_offset = ec_fsm_master_dc_offset64(fsm,
+ system_time, old_offset, jiffies_since_read);
+ }
+
+ // set DC system time offset and transmission delay
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0920, 12);
+ EC_WRITE_U64(datagram->data, new_offset);
+ EC_WRITE_U32(datagram->data + 8, slave->transmission_delay);
+ fsm->datagram->device_index = slave->device_index;
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_master_state_dc_write_offset;
+}
+
+/*****************************************************************************/
+
+/** Master state: DC WRITE OFFSET.
+ */
+void ec_fsm_master_state_dc_write_offset(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_ERR(slave,
+ "Failed to receive DC system time offset datagram: ");
+ ec_datagram_print_state(datagram);
+ fsm->slave++;
+ ec_fsm_master_enter_write_system_times(fsm);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_ERR(slave, "Failed to set DC system time offset: ");
+ ec_datagram_print_wc_error(datagram);
+ fsm->slave++;
+ ec_fsm_master_enter_write_system_times(fsm);
+ return;
+ }
+
+ fsm->slave++;
+ ec_fsm_master_enter_write_system_times(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: WRITE SII.
+ */
+void ec_fsm_master_state_write_sii(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_master_t *master = fsm->master;
+ ec_sii_write_request_t *request = fsm->sii_request;
+ ec_slave_t *slave = request->slave;
+
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
+ EC_SLAVE_ERR(slave, "Failed to write SII data.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&master->request_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ fsm->sii_index++;
+ if (fsm->sii_index < request->nwords) {
+ ec_fsm_sii_write(&fsm->fsm_sii, slave,
+ request->offset + fsm->sii_index,
+ request->words + fsm->sii_index,
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute immediately
+ return;
+ }
+
+ // finished writing SII
+ EC_SLAVE_DBG(slave, 1, "Finished writing %zu words of SII data.\n",
+ request->nwords);
+
+ if (request->offset <= 4 && request->offset + request->nwords > 4) {
+ // alias was written
+ slave->sii.alias = EC_READ_U16(request->words + 4);
+ // TODO: read alias from register 0x0012
+ slave->effective_alias = slave->sii.alias;
+ }
+ // TODO: Evaluate other SII contents!
+
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&master->request_queue);
+
+ // check for another SII write request
+ if (ec_fsm_master_action_process_sii(fsm))
+ return; // processing another request
+
+ ec_fsm_master_restart(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: SDO DICTIONARY.
+ */
+void ec_fsm_master_state_sdo_dictionary(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = fsm->master;
+
+ if (ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ // SDO dictionary fetching finished
+
+ if (master->debug_level) {
+ unsigned int sdo_count, entry_count;
+ ec_slave_sdo_dict_info(slave, &sdo_count, &entry_count);
+ EC_SLAVE_DBG(slave, 1, "Fetched %u SDOs and %u entries.\n",
+ sdo_count, entry_count);
+ }
+
+ // attach pdo names from dictionary
+ ec_slave_attach_pdo_names(slave);
+
+ ec_fsm_master_restart(fsm);
+}
+
+/*****************************************************************************/
+
+/** Master state: SDO REQUEST.
+ */
+void ec_fsm_master_state_sdo_request(
+ ec_fsm_master_t *fsm /**< Master state machine. */
+ )
+{
+ ec_sdo_request_t *request = fsm->sdo_request;
+
+ if (!request) {
+ // configuration was cleared in the meantime
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ if (ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
+ EC_SLAVE_DBG(fsm->slave, 1,
+ "Failed to process internal SDO request.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&fsm->master->request_queue);
+ ec_fsm_master_restart(fsm);
+ return;
+ }
+
+ // SDO request finished
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&fsm->master->request_queue);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Finished internal SDO request.\n");
+
+ // check for another SDO request
+ if (ec_fsm_master_action_process_sdo(fsm)) {
+ return; // processing another request
+ }
+
+ ec_fsm_master_restart(fsm);
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_master.h b/net/ethercat/master/fsm_master.h
new file mode 100644
index 000000000000..a41b949c1b97
--- /dev/null
+++ b/net/ethercat/master/fsm_master.h
@@ -0,0 +1,113 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master state machine.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_MASTER_H__
+#define __EC_FSM_MASTER_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "foe_request.h"
+#include "sdo_request.h"
+#include "soe_request.h"
+#include "fsm_slave_config.h"
+#include "fsm_slave_scan.h"
+#include "fsm_pdo.h"
+
+/*****************************************************************************/
+
+/** SII write request.
+ */
+typedef struct {
+ struct list_head list; /**< List head. */
+ ec_slave_t *slave; /**< EtherCAT slave. */
+ uint16_t offset; /**< SII word offset. */
+ size_t nwords; /**< Number of words. */
+ const uint16_t *words; /**< Pointer to the data words. */
+ ec_internal_request_state_t state; /**< State of the request. */
+} ec_sii_write_request_t;
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_master ec_fsm_master_t; /**< \see ec_fsm_master */
+
+/** Finite state machine of an EtherCAT master.
+ */
+struct ec_fsm_master {
+ ec_master_t *master; /**< master the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+ unsigned int retries; /**< retries on datagram timeout. */
+
+ void (*state)(ec_fsm_master_t *); /**< master state function */
+ ec_device_index_t dev_idx; /**< Current device index (for scanning etc.).
+ */
+ int idle; /**< state machine is in idle phase */
+ unsigned long scan_jiffies; /**< beginning of slave scanning */
+ uint8_t link_state[EC_MAX_NUM_DEVICES]; /**< Last link state for every
+ device. */
+ unsigned int slaves_responding[EC_MAX_NUM_DEVICES]; /**< Number of
+ responding slaves
+ for every device. */
+ unsigned int rescan_required; /**< A bus rescan is required. */
+ ec_slave_state_t slave_states[EC_MAX_NUM_DEVICES]; /**< AL states of
+ responding slaves for
+ every device. */
+ ec_slave_t *slave; /**< current slave */
+ ec_sii_write_request_t *sii_request; /**< SII write request */
+ off_t sii_index; /**< index to SII write request data */
+ ec_sdo_request_t *sdo_request; /**< SDO request to process. */
+
+ ec_fsm_coe_t fsm_coe; /**< CoE state machine */
+ ec_fsm_soe_t fsm_soe; /**< SoE state machine */
+ ec_fsm_pdo_t fsm_pdo; /**< PDO configuration state machine. */
+ ec_fsm_change_t fsm_change; /**< State change state machine */
+ ec_fsm_slave_config_t fsm_slave_config; /**< slave state machine */
+ ec_fsm_slave_scan_t fsm_slave_scan; /**< slave state machine */
+ ec_fsm_sii_t fsm_sii; /**< SII state machine */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_master_init(ec_fsm_master_t *, ec_master_t *, ec_datagram_t *);
+void ec_fsm_master_clear(ec_fsm_master_t *);
+
+void ec_fsm_master_reset(ec_fsm_master_t *);
+
+int ec_fsm_master_exec(ec_fsm_master_t *);
+int ec_fsm_master_idle(const ec_fsm_master_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_pdo.c b/net/ethercat/master/fsm_pdo.c
new file mode 100644
index 000000000000..d6fe964ad33d
--- /dev/null
+++ b/net/ethercat/master/fsm_pdo.c
@@ -0,0 +1,806 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO configuration state machine.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+
+#include "fsm_pdo.h"
+
+/*****************************************************************************/
+
+void ec_fsm_pdo_read_state_start(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_read_state_pdo_count(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_read_state_pdo(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_read_state_pdo_entries(ec_fsm_pdo_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_read_action_next_sync(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_read_action_next_pdo(ec_fsm_pdo_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_conf_state_start(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_state_read_mapping(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_state_mapping(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_state_zero_pdo_count(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_state_assign_pdo(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_state_set_pdo_count(ec_fsm_pdo_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_conf_action_next_sync(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_action_pdo_mapping(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_action_check_mapping(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_action_next_pdo_mapping(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_action_check_assignment(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_conf_action_assign_pdo(ec_fsm_pdo_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_state_end(ec_fsm_pdo_t *, ec_datagram_t *);
+void ec_fsm_pdo_state_error(ec_fsm_pdo_t *, ec_datagram_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_pdo_init(
+ ec_fsm_pdo_t *fsm, /**< PDO configuration state machine. */
+ ec_fsm_coe_t *fsm_coe /**< CoE state machine to use */
+ )
+{
+ fsm->fsm_coe = fsm_coe;
+ ec_fsm_pdo_entry_init(&fsm->fsm_pdo_entry, fsm_coe);
+ ec_pdo_list_init(&fsm->pdos);
+ ec_sdo_request_init(&fsm->request);
+ ec_pdo_init(&fsm->slave_pdo);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_pdo_clear(
+ ec_fsm_pdo_t *fsm /**< PDO configuration state machine. */
+ )
+{
+ ec_fsm_pdo_entry_clear(&fsm->fsm_pdo_entry);
+ ec_pdo_list_clear(&fsm->pdos);
+ ec_sdo_request_clear(&fsm->request);
+ ec_pdo_clear(&fsm->slave_pdo);
+}
+
+/*****************************************************************************/
+
+/** Print the current and desired PDO assignment.
+ */
+void ec_fsm_pdo_print(
+ ec_fsm_pdo_t *fsm /**< PDO configuration state machine. */
+ )
+{
+ printk("Currently assigned PDOs: ");
+ ec_pdo_list_print(&fsm->sync->pdos);
+ printk(". PDOs to assign: ");
+ ec_pdo_list_print(&fsm->pdos);
+ printk("\n");
+}
+
+/*****************************************************************************/
+
+/** Start reading the PDO configuration.
+ */
+void ec_fsm_pdo_start_reading(
+ ec_fsm_pdo_t *fsm, /**< PDO configuration state machine. */
+ ec_slave_t *slave /**< slave to configure */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_pdo_read_state_start;
+}
+
+/*****************************************************************************/
+
+/** Start writing the PDO configuration.
+ */
+void ec_fsm_pdo_start_configuration(
+ ec_fsm_pdo_t *fsm, /**< PDO configuration state machine. */
+ ec_slave_t *slave /**< slave to configure */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_pdo_conf_state_start;
+}
+
+/*****************************************************************************/
+
+/** Get running state.
+ *
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_pdo_running(
+ const ec_fsm_pdo_t *fsm /**< PDO configuration state machine. */
+ )
+{
+ return fsm->state != ec_fsm_pdo_state_end
+ && fsm->state != ec_fsm_pdo_state_error;
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * If the state machine's datagram is not sent or received yet, the execution
+ * of the state machine is delayed to the next cycle.
+ *
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_pdo_exec(
+ ec_fsm_pdo_t *fsm, /**< PDO configuration state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ fsm->state(fsm, datagram);
+
+ return ec_fsm_pdo_running(fsm);
+}
+
+/*****************************************************************************/
+
+/** Get execution result.
+ *
+ * \return true, if the state machine terminated gracefully
+ */
+int ec_fsm_pdo_success(
+ const ec_fsm_pdo_t *fsm /**< PDO configuration state machine. */
+ )
+{
+ return fsm->state == ec_fsm_pdo_state_end;
+}
+
+/******************************************************************************
+ * Reading state funtions.
+ *****************************************************************************/
+
+/** Start reading PDO assignment.
+ */
+void ec_fsm_pdo_read_state_start(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ // read PDO assignment for first sync manager not reserved for mailbox
+ fsm->sync_index = 1; // next is 2
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Read PDO assignment of next sync manager.
+ */
+void ec_fsm_pdo_read_action_next_sync(
+ ec_fsm_pdo_t *fsm, /**< finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ fsm->sync_index++;
+
+ for (; fsm->sync_index < EC_MAX_SYNC_MANAGERS; fsm->sync_index++) {
+ if (!(fsm->sync = ec_slave_get_sync(slave, fsm->sync_index)))
+ continue;
+
+ EC_SLAVE_DBG(slave, 1, "Reading PDO assignment of SM%u.\n",
+ fsm->sync_index);
+
+ ec_pdo_list_clear_pdos(&fsm->pdos);
+
+ ecrt_sdo_request_index(&fsm->request, 0x1C10 + fsm->sync_index, 0);
+ ecrt_sdo_request_read(&fsm->request);
+ fsm->state = ec_fsm_pdo_read_state_pdo_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Reading of PDO configuration finished.\n");
+
+ ec_pdo_list_clear_pdos(&fsm->pdos);
+ fsm->state = ec_fsm_pdo_state_end;
+}
+
+/*****************************************************************************/
+
+/** Count assigned PDOs.
+ */
+void ec_fsm_pdo_read_state_pdo_count(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to read number of assigned PDOs"
+ " for SM%u.\n", fsm->sync_index);
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ if (fsm->request.data_size != sizeof(uint8_t)) {
+ EC_SLAVE_ERR(fsm->slave, "Invalid data size %zu returned"
+ " when uploading SDO 0x%04X:%02X.\n", fsm->request.data_size,
+ fsm->request.index, fsm->request.subindex);
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+ fsm->pdo_count = EC_READ_U8(fsm->request.data);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "%u PDOs assigned.\n", fsm->pdo_count);
+
+ // read first PDO
+ fsm->pdo_pos = 1;
+ ec_fsm_pdo_read_action_next_pdo(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Read next PDO.
+ */
+void ec_fsm_pdo_read_action_next_pdo(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (fsm->pdo_pos <= fsm->pdo_count) {
+ ecrt_sdo_request_index(&fsm->request, 0x1C10 + fsm->sync_index,
+ fsm->pdo_pos);
+ ecrt_sdo_request_read(&fsm->request);
+ fsm->state = ec_fsm_pdo_read_state_pdo;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+
+ // finished reading PDO configuration
+
+ ec_pdo_list_copy(&fsm->sync->pdos, &fsm->pdos);
+ ec_pdo_list_clear_pdos(&fsm->pdos);
+
+ // next sync manager
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Fetch PDO information.
+ */
+void ec_fsm_pdo_read_state_pdo(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to read index of"
+ " assigned PDO %u from SM%u.\n",
+ fsm->pdo_pos, fsm->sync_index);
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ if (fsm->request.data_size != sizeof(uint16_t)) {
+ EC_SLAVE_ERR(fsm->slave, "Invalid data size %zu returned"
+ " when uploading SDO 0x%04X:%02X.\n", fsm->request.data_size,
+ fsm->request.index, fsm->request.subindex);
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ if (!(fsm->pdo = (ec_pdo_t *)
+ kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to allocate PDO.\n");
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ ec_pdo_init(fsm->pdo);
+ fsm->pdo->index = EC_READ_U16(fsm->request.data);
+ fsm->pdo->sync_index = fsm->sync_index;
+
+ EC_SLAVE_DBG(fsm->slave, 1, "PDO 0x%04X.\n", fsm->pdo->index);
+
+ list_add_tail(&fsm->pdo->list, &fsm->pdos.list);
+
+ fsm->state = ec_fsm_pdo_read_state_pdo_entries;
+ ec_fsm_pdo_entry_start_reading(&fsm->fsm_pdo_entry, fsm->slave, fsm->pdo);
+ fsm->state(fsm, datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Fetch PDO information.
+ */
+void ec_fsm_pdo_read_state_pdo_entries(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_pdo_entry_exec(&fsm->fsm_pdo_entry, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_pdo_entry_success(&fsm->fsm_pdo_entry)) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to read mapped PDO entries"
+ " for PDO 0x%04X.\n", fsm->pdo->index);
+ ec_fsm_pdo_read_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ // next PDO
+ fsm->pdo_pos++;
+ ec_fsm_pdo_read_action_next_pdo(fsm, datagram);
+}
+
+/******************************************************************************
+ * Writing state functions.
+ *****************************************************************************/
+
+/** Start PDO configuration.
+ */
+void ec_fsm_pdo_conf_state_start(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (!fsm->slave->config) {
+ fsm->state = ec_fsm_pdo_state_end;
+ return;
+ }
+
+ fsm->sync_index = 1; // next is 2
+ ec_fsm_pdo_conf_action_next_sync(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Assign next PDO.
+ *
+ * \return Next PDO, or NULL.
+ */
+ec_pdo_t *ec_fsm_pdo_conf_action_next_pdo(
+ const ec_fsm_pdo_t *fsm, /**< PDO configuration state machine. */
+ const struct list_head *list /**< current PDO list item */
+ )
+{
+ list = list->next;
+ if (list == &fsm->pdos.list)
+ return NULL; // no next PDO
+ return list_entry(list, ec_pdo_t, list);
+}
+
+/*****************************************************************************/
+
+/** Get the next sync manager for a pdo configuration.
+ */
+void ec_fsm_pdo_conf_action_next_sync(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ fsm->sync_index++;
+
+ for (; fsm->sync_index < EC_MAX_SYNC_MANAGERS; fsm->sync_index++) {
+ if (!fsm->slave->config) {
+ // slave configuration removed in the meantime
+ fsm->state = ec_fsm_pdo_state_error;
+ return;
+ }
+
+ if (ec_pdo_list_copy(&fsm->pdos,
+ &fsm->slave->config->sync_configs[fsm->sync_index].pdos))
+ {
+ fsm->state = ec_fsm_pdo_state_error;
+ return;
+ }
+
+ if (!(fsm->sync = ec_slave_get_sync(fsm->slave, fsm->sync_index))) {
+ if (!list_empty(&fsm->pdos.list))
+ EC_SLAVE_WARN(fsm->slave, "PDOs configured for SM%u,"
+ " but slave does not provide the"
+ " sync manager information!\n",
+ fsm->sync_index);
+ continue;
+ }
+
+ // get first configured PDO
+ if (!(fsm->pdo =
+ ec_fsm_pdo_conf_action_next_pdo(fsm, &fsm->pdos.list))) {
+ // no pdos configured
+ ec_fsm_pdo_conf_action_check_assignment(fsm, datagram);
+ return;
+ }
+
+ ec_fsm_pdo_conf_action_pdo_mapping(fsm, datagram);
+ return;
+ }
+
+ fsm->state = ec_fsm_pdo_state_end;
+}
+
+/*****************************************************************************/
+
+/** Check if the mapping has to be read, otherwise start to configure it.
+ */
+void ec_fsm_pdo_conf_action_pdo_mapping(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ const ec_pdo_t *assigned_pdo;
+
+ fsm->slave_pdo.index = fsm->pdo->index;
+
+ if ((assigned_pdo = ec_slave_find_pdo(fsm->slave, fsm->pdo->index))) {
+ ec_pdo_copy_entries(&fsm->slave_pdo, assigned_pdo);
+ } else { // configured PDO is not assigned and thus unknown
+ ec_pdo_clear_entries(&fsm->slave_pdo);
+ }
+
+ if (list_empty(&fsm->slave_pdo.entries)) {
+ EC_SLAVE_DBG(fsm->slave, 1, "Reading mapping of PDO 0x%04X.\n",
+ fsm->pdo->index);
+
+ // pdo mapping is unknown; start loading it
+ ec_fsm_pdo_entry_start_reading(&fsm->fsm_pdo_entry, fsm->slave,
+ &fsm->slave_pdo);
+ fsm->state = ec_fsm_pdo_conf_state_read_mapping;
+ fsm->state(fsm, datagram); // execute immediately
+ return;
+ }
+
+ // pdo mapping is known, check if it most be re-configured
+ ec_fsm_pdo_conf_action_check_mapping(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Execute the PDO entry state machine to read the current PDO's mapping.
+ */
+void ec_fsm_pdo_conf_state_read_mapping(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_pdo_entry_exec(&fsm->fsm_pdo_entry, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_pdo_entry_success(&fsm->fsm_pdo_entry))
+ EC_SLAVE_WARN(fsm->slave,
+ "Failed to read PDO entries for PDO 0x%04X.\n",
+ fsm->pdo->index);
+
+ // check if the mapping must be re-configured
+ ec_fsm_pdo_conf_action_check_mapping(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Check if the mapping has to be re-configured.
+ *
+ * \todo Display mapping differences.
+ */
+void ec_fsm_pdo_conf_action_check_mapping(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ // check, if slave supports PDO configuration
+ if ((fsm->slave->sii.mailbox_protocols & EC_MBOX_COE)
+ && fsm->slave->sii.has_general
+ && fsm->slave->sii.coe_details.enable_pdo_configuration) {
+
+ // always write PDO mapping
+ ec_fsm_pdo_entry_start_configuration(&fsm->fsm_pdo_entry, fsm->slave,
+ fsm->pdo, &fsm->slave_pdo);
+ fsm->state = ec_fsm_pdo_conf_state_mapping;
+ fsm->state(fsm, datagram); // execure immediately
+ return;
+ }
+ else if (!ec_pdo_equal_entries(fsm->pdo, &fsm->slave_pdo)) {
+ EC_SLAVE_WARN(fsm->slave, "Slave does not support"
+ " changing the PDO mapping!\n");
+ EC_SLAVE_WARN(fsm->slave, "");
+ printk("Currently mapped PDO entries: ");
+ ec_pdo_print_entries(&fsm->slave_pdo);
+ printk(". Entries to map: ");
+ ec_pdo_print_entries(fsm->pdo);
+ printk("\n");
+ }
+
+ ec_fsm_pdo_conf_action_next_pdo_mapping(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Let the PDO entry state machine configure the current PDO's mapping.
+ */
+void ec_fsm_pdo_conf_state_mapping(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_pdo_entry_exec(&fsm->fsm_pdo_entry, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_pdo_entry_success(&fsm->fsm_pdo_entry))
+ EC_SLAVE_WARN(fsm->slave,
+ "Failed to configure mapping of PDO 0x%04X.\n",
+ fsm->pdo->index);
+
+ ec_fsm_pdo_conf_action_next_pdo_mapping(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Check mapping of next PDO, otherwise configure assignment.
+ */
+void ec_fsm_pdo_conf_action_next_pdo_mapping(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ // get next configured PDO
+ if (!(fsm->pdo = ec_fsm_pdo_conf_action_next_pdo(fsm, &fsm->pdo->list))) {
+ // no more configured pdos
+ ec_fsm_pdo_conf_action_check_assignment(fsm, datagram);
+ return;
+ }
+
+ ec_fsm_pdo_conf_action_pdo_mapping(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Check if the PDO assignment of the current SM has to be re-configured.
+ */
+void ec_fsm_pdo_conf_action_check_assignment(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if ((fsm->slave->sii.mailbox_protocols & EC_MBOX_COE)
+ && fsm->slave->sii.has_general
+ && fsm->slave->sii.coe_details.enable_pdo_assign) {
+
+ // always write PDO assignment
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(fsm->slave, 1, "Setting PDO assignment of SM%u:\n",
+ fsm->sync_index);
+ EC_SLAVE_DBG(fsm->slave, 1, ""); ec_fsm_pdo_print(fsm);
+ }
+
+ if (ec_sdo_request_alloc(&fsm->request, 2)) {
+ fsm->state = ec_fsm_pdo_state_error;
+ return;
+ }
+
+ // set mapped PDO count to zero
+ EC_WRITE_U8(fsm->request.data, 0); // zero PDOs mapped
+ fsm->request.data_size = 1;
+ ecrt_sdo_request_index(&fsm->request, 0x1C10 + fsm->sync_index, 0);
+ ecrt_sdo_request_write(&fsm->request);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Setting number of assigned"
+ " PDOs to zero.\n");
+
+ fsm->state = ec_fsm_pdo_conf_state_zero_pdo_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+ else if (!ec_pdo_list_equal(&fsm->sync->pdos, &fsm->pdos)) {
+ EC_SLAVE_WARN(fsm->slave, "Slave does not support assigning PDOs!\n");
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_print(fsm);
+ }
+
+ ec_fsm_pdo_conf_action_next_sync(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Set the number of assigned PDOs to zero.
+ */
+void ec_fsm_pdo_conf_state_zero_pdo_count(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to clear PDO assignment of SM%u.\n",
+ fsm->sync_index);
+ EC_SLAVE_WARN(fsm->slave, "");
+ ec_fsm_pdo_print(fsm);
+ ec_fsm_pdo_conf_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ // the sync manager's assigned PDOs have been cleared
+ ec_pdo_list_clear_pdos(&fsm->sync->pdos);
+
+ // assign all PDOs belonging to the current sync manager
+
+ // find first PDO
+ if (!(fsm->pdo = ec_fsm_pdo_conf_action_next_pdo(fsm, &fsm->pdos.list))) {
+ // check for mapping to be altered
+ ec_fsm_pdo_conf_action_next_sync(fsm, datagram);
+ return;
+ }
+
+ // assign first PDO
+ fsm->pdo_pos = 1;
+ ec_fsm_pdo_conf_action_assign_pdo(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Assign a PDO.
+ */
+void ec_fsm_pdo_conf_action_assign_pdo(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ EC_WRITE_U16(fsm->request.data, fsm->pdo->index);
+ fsm->request.data_size = 2;
+ ecrt_sdo_request_index(&fsm->request,
+ 0x1C10 + fsm->sync_index, fsm->pdo_pos);
+ ecrt_sdo_request_write(&fsm->request);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Assigning PDO 0x%04X at position %u.\n",
+ fsm->pdo->index, fsm->pdo_pos);
+
+ fsm->state = ec_fsm_pdo_conf_state_assign_pdo;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Add a PDO to the sync managers PDO assignment.
+ */
+void ec_fsm_pdo_conf_state_assign_pdo(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to assign PDO 0x%04X at position %u"
+ " of SM%u.\n",
+ fsm->pdo->index, fsm->pdo_pos, fsm->sync_index);
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_print(fsm);
+ fsm->state = ec_fsm_pdo_state_error;
+ return;
+ }
+
+ // find next PDO
+ if (!(fsm->pdo = ec_fsm_pdo_conf_action_next_pdo(fsm, &fsm->pdo->list))) {
+ // no more PDOs to assign, set PDO count
+ EC_WRITE_U8(fsm->request.data, fsm->pdo_pos);
+ fsm->request.data_size = 1;
+ ecrt_sdo_request_index(&fsm->request, 0x1C10 + fsm->sync_index, 0);
+ ecrt_sdo_request_write(&fsm->request);
+
+ EC_SLAVE_DBG(fsm->slave, 1,
+ "Setting number of assigned PDOs to %u.\n",
+ fsm->pdo_pos);
+
+ fsm->state = ec_fsm_pdo_conf_state_set_pdo_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+
+ // add next PDO to assignment
+ fsm->pdo_pos++;
+ ec_fsm_pdo_conf_action_assign_pdo(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Set the number of assigned PDOs.
+ */
+void ec_fsm_pdo_conf_state_set_pdo_count(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to set number of"
+ " assigned PDOs of SM%u.\n", fsm->sync_index);
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_print(fsm);
+ fsm->state = ec_fsm_pdo_state_error;
+ return;
+ }
+
+ // PDOs have been configured
+ ec_pdo_list_copy(&fsm->sync->pdos, &fsm->pdos);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Successfully configured"
+ " PDO assignment of SM%u.\n", fsm->sync_index);
+
+ // check if PDO mapping has to be altered
+ ec_fsm_pdo_conf_action_next_sync(fsm, datagram);
+}
+
+/******************************************************************************
+ * Common state functions
+ *****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_pdo_state_error(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_pdo_state_end(
+ ec_fsm_pdo_t *fsm, /**< Finite state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_pdo.h b/net/ethercat/master/fsm_pdo.h
new file mode 100644
index 000000000000..bd3b36565a9f
--- /dev/null
+++ b/net/ethercat/master/fsm_pdo.h
@@ -0,0 +1,84 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO configuration state machine structures.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_PDO_H__
+#define __EC_FSM_PDO_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "fsm_coe.h"
+#include "fsm_pdo_entry.h"
+
+/*****************************************************************************/
+
+/**
+ * \see ec_fsm_pdo
+ */
+typedef struct ec_fsm_pdo ec_fsm_pdo_t;
+
+/** PDO configuration state machine.
+ */
+struct ec_fsm_pdo
+{
+ void (*state)(ec_fsm_pdo_t *, ec_datagram_t *); /**< State function. */
+ ec_fsm_coe_t *fsm_coe; /**< CoE state machine to use. */
+ ec_fsm_pdo_entry_t fsm_pdo_entry; /**< PDO entry state machine. */
+ ec_pdo_list_t pdos; /**< PDO configuration. */
+ ec_sdo_request_t request; /**< SDO request. */
+ ec_pdo_t slave_pdo; /**< PDO actually appearing in a slave. */
+
+ ec_slave_t *slave; /**< Slave the FSM runs on. */
+ uint8_t sync_index; /**< Current sync manager index. */
+ ec_sync_t *sync; /**< Current sync manager. */
+ ec_pdo_t *pdo; /**< Current PDO. */
+ unsigned int pdo_pos; /**< Assignment position of current PDOs. */
+ unsigned int pdo_count; /**< Number of assigned PDOs. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_pdo_init(ec_fsm_pdo_t *, ec_fsm_coe_t *);
+void ec_fsm_pdo_clear(ec_fsm_pdo_t *);
+
+void ec_fsm_pdo_start_reading(ec_fsm_pdo_t *, ec_slave_t *);
+void ec_fsm_pdo_start_configuration(ec_fsm_pdo_t *, ec_slave_t *);
+
+int ec_fsm_pdo_exec(ec_fsm_pdo_t *, ec_datagram_t *);
+int ec_fsm_pdo_success(const ec_fsm_pdo_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_pdo_entry.c b/net/ethercat/master/fsm_pdo_entry.c
new file mode 100644
index 000000000000..16208d2664be
--- /dev/null
+++ b/net/ethercat/master/fsm_pdo_entry.c
@@ -0,0 +1,541 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO mapping state machine.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+
+#include "fsm_pdo_entry.h"
+
+/*****************************************************************************/
+
+void ec_fsm_pdo_entry_read_state_start(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+void ec_fsm_pdo_entry_read_state_count(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+void ec_fsm_pdo_entry_read_state_entry(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_entry_read_action_next(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_entry_conf_state_start(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+void ec_fsm_pdo_entry_conf_state_zero_entry_count(ec_fsm_pdo_entry_t *,
+ ec_datagram_t *);
+void ec_fsm_pdo_entry_conf_state_map_entry(ec_fsm_pdo_entry_t *,
+ ec_datagram_t *);
+void ec_fsm_pdo_entry_conf_state_set_entry_count(ec_fsm_pdo_entry_t *,
+ ec_datagram_t *);
+
+void ec_fsm_pdo_entry_conf_action_map(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+
+void ec_fsm_pdo_entry_state_end(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+void ec_fsm_pdo_entry_state_error(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_pdo_entry_init(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_fsm_coe_t *fsm_coe /**< CoE state machine to use. */
+ )
+{
+ fsm->fsm_coe = fsm_coe;
+ ec_sdo_request_init(&fsm->request);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_pdo_entry_clear(
+ ec_fsm_pdo_entry_t *fsm /**< PDO mapping state machine. */
+ )
+{
+ ec_sdo_request_clear(&fsm->request);
+}
+
+/*****************************************************************************/
+
+/** Print the current and desired PDO mapping.
+ */
+void ec_fsm_pdo_entry_print(
+ ec_fsm_pdo_entry_t *fsm /**< PDO mapping state machine. */
+ )
+{
+ printk("Currently mapped PDO entries: ");
+ ec_pdo_print_entries(fsm->cur_pdo);
+ printk(". Entries to map: ");
+ ec_pdo_print_entries(fsm->source_pdo);
+ printk("\n");
+}
+
+/*****************************************************************************/
+
+/** Start reading a PDO's entries.
+ */
+void ec_fsm_pdo_entry_start_reading(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_slave_t *slave, /**< Slave to configure. */
+ ec_pdo_t *pdo /**< PDO to read entries for. */
+ )
+{
+ fsm->slave = slave;
+ fsm->target_pdo = pdo;
+
+ ec_pdo_clear_entries(fsm->target_pdo);
+
+ fsm->state = ec_fsm_pdo_entry_read_state_start;
+}
+
+/*****************************************************************************/
+
+/** Start PDO mapping state machine.
+ */
+void ec_fsm_pdo_entry_start_configuration(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_slave_t *slave, /**< Slave to configure. */
+ const ec_pdo_t *pdo, /**< PDO with the desired entries. */
+ const ec_pdo_t *cur_pdo /**< Current PDO mapping. */
+ )
+{
+ fsm->slave = slave;
+ fsm->source_pdo = pdo;
+ fsm->cur_pdo = cur_pdo;
+
+ if (fsm->slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 1, "Changing mapping of PDO 0x%04X.\n",
+ pdo->index);
+ EC_SLAVE_DBG(slave, 1, ""); ec_fsm_pdo_entry_print(fsm);
+ }
+
+ fsm->state = ec_fsm_pdo_entry_conf_state_start;
+}
+
+/*****************************************************************************/
+
+/** Get running state.
+ *
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_pdo_entry_running(
+ const ec_fsm_pdo_entry_t *fsm /**< PDO mapping state machine. */
+ )
+{
+ return fsm->state != ec_fsm_pdo_entry_state_end
+ && fsm->state != ec_fsm_pdo_entry_state_error;
+}
+
+/*****************************************************************************/
+
+/** Executes the current state.
+ *
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_pdo_entry_exec(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ fsm->state(fsm, datagram);
+
+ return ec_fsm_pdo_entry_running(fsm);
+}
+
+/*****************************************************************************/
+
+/** Get execution result.
+ *
+ * \return true, if the state machine terminated gracefully
+ */
+int ec_fsm_pdo_entry_success(
+ const ec_fsm_pdo_entry_t *fsm /**< PDO mapping state machine. */
+ )
+{
+ return fsm->state == ec_fsm_pdo_entry_state_end;
+}
+
+/******************************************************************************
+ * Reading state functions.
+ *****************************************************************************/
+
+/** Request reading the number of mapped PDO entries.
+ */
+void ec_fsm_pdo_entry_read_state_start(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ecrt_sdo_request_index(&fsm->request, fsm->target_pdo->index, 0);
+ ecrt_sdo_request_read(&fsm->request);
+
+ fsm->state = ec_fsm_pdo_entry_read_state_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Read number of mapped PDO entries.
+ */
+void ec_fsm_pdo_entry_read_state_count(
+ ec_fsm_pdo_entry_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_ERR(fsm->slave,
+ "Failed to read number of mapped PDO entries.\n");
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ if (fsm->request.data_size != sizeof(uint8_t)) {
+ EC_SLAVE_ERR(fsm->slave, "Invalid data size %zu at uploading"
+ " SDO 0x%04X:%02X.\n",
+ fsm->request.data_size, fsm->request.index,
+ fsm->request.subindex);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ fsm->entry_count = EC_READ_U8(fsm->request.data);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "%u PDO entries mapped.\n", fsm->entry_count);
+
+ // read first PDO entry
+ fsm->entry_pos = 1;
+ ec_fsm_pdo_entry_read_action_next(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Read next PDO entry.
+ */
+void ec_fsm_pdo_entry_read_action_next(
+ ec_fsm_pdo_entry_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (fsm->entry_pos <= fsm->entry_count) {
+ ecrt_sdo_request_index(&fsm->request, fsm->target_pdo->index,
+ fsm->entry_pos);
+ ecrt_sdo_request_read(&fsm->request);
+ fsm->state = ec_fsm_pdo_entry_read_state_entry;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+
+ // finished reading entries.
+ fsm->state = ec_fsm_pdo_entry_state_end;
+}
+
+/*****************************************************************************/
+
+/** Read PDO entry information.
+ */
+void ec_fsm_pdo_entry_read_state_entry(
+ ec_fsm_pdo_entry_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to read mapped PDO entry.\n");
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ if (fsm->request.data_size != sizeof(uint32_t)) {
+ EC_SLAVE_ERR(fsm->slave, "Invalid data size %zu at"
+ " uploading SDO 0x%04X:%02X.\n",
+ fsm->request.data_size, fsm->request.index,
+ fsm->request.subindex);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ } else {
+ uint32_t pdo_entry_info;
+ ec_pdo_entry_t *pdo_entry;
+
+ pdo_entry_info = EC_READ_U32(fsm->request.data);
+
+ if (!(pdo_entry = (ec_pdo_entry_t *)
+ kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(fsm->slave, "Failed to allocate PDO entry.\n");
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ ec_pdo_entry_init(pdo_entry);
+ pdo_entry->index = pdo_entry_info >> 16;
+ pdo_entry->subindex = (pdo_entry_info >> 8) & 0xFF;
+ pdo_entry->bit_length = pdo_entry_info & 0xFF;
+
+ if (!pdo_entry->index && !pdo_entry->subindex) {
+ if (ec_pdo_entry_set_name(pdo_entry, "Gap")) {
+ ec_pdo_entry_clear(pdo_entry);
+ kfree(pdo_entry);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+ }
+
+ EC_SLAVE_DBG(fsm->slave, 1,
+ "PDO entry 0x%04X:%02X, %u bit, \"%s\".\n",
+ pdo_entry->index, pdo_entry->subindex,
+ pdo_entry->bit_length,
+ pdo_entry->name ? pdo_entry->name : "???");
+
+ list_add_tail(&pdo_entry->list, &fsm->target_pdo->entries);
+
+ // next PDO entry
+ fsm->entry_pos++;
+ ec_fsm_pdo_entry_read_action_next(fsm, datagram);
+ }
+}
+
+/******************************************************************************
+ * Configuration state functions.
+ *****************************************************************************/
+
+/** Start PDO mapping.
+ */
+void ec_fsm_pdo_entry_conf_state_start(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_sdo_request_alloc(&fsm->request, 4)) {
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ // set mapped PDO entry count to zero
+ EC_WRITE_U8(fsm->request.data, 0);
+ fsm->request.data_size = 1;
+ ecrt_sdo_request_index(&fsm->request, fsm->source_pdo->index, 0);
+ ecrt_sdo_request_write(&fsm->request);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Setting entry count to zero.\n");
+
+ fsm->state = ec_fsm_pdo_entry_conf_state_zero_entry_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Process next PDO entry.
+ *
+ * \return Next PDO entry, or NULL.
+ */
+ec_pdo_entry_t *ec_fsm_pdo_entry_conf_next_entry(
+ const ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ const struct list_head *list /**< current entry list item */
+ )
+{
+ list = list->next;
+ if (list == &fsm->source_pdo->entries)
+ return NULL; // no next entry
+ return list_entry(list, ec_pdo_entry_t, list);
+}
+
+/*****************************************************************************/
+
+/** Set the number of mapped entries to zero.
+ */
+void ec_fsm_pdo_entry_conf_state_zero_entry_count(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to clear PDO mapping.\n");
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_entry_print(fsm);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ // find first entry
+ if (!(fsm->entry = ec_fsm_pdo_entry_conf_next_entry(
+ fsm, &fsm->source_pdo->entries))) {
+
+ EC_SLAVE_DBG(fsm->slave, 1, "No entries to map.\n");
+
+ fsm->state = ec_fsm_pdo_entry_state_end; // finished
+ return;
+ }
+
+ // add first entry
+ fsm->entry_pos = 1;
+ ec_fsm_pdo_entry_conf_action_map(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Starts to add a PDO entry.
+ */
+void ec_fsm_pdo_entry_conf_action_map(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ uint32_t value;
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Mapping PDO entry 0x%04X:%02X (%u bit)"
+ " at position %u.\n",
+ fsm->entry->index, fsm->entry->subindex,
+ fsm->entry->bit_length, fsm->entry_pos);
+
+ value = fsm->entry->index << 16
+ | fsm->entry->subindex << 8 | fsm->entry->bit_length;
+ EC_WRITE_U32(fsm->request.data, value);
+ fsm->request.data_size = 4;
+ ecrt_sdo_request_index(&fsm->request, fsm->source_pdo->index,
+ fsm->entry_pos);
+ ecrt_sdo_request_write(&fsm->request);
+
+ fsm->state = ec_fsm_pdo_entry_conf_state_map_entry;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Add a PDO entry.
+ */
+void ec_fsm_pdo_entry_conf_state_map_entry(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to map PDO entry"
+ " 0x%04X:%02X (%u bit) to position %u.\n",
+ fsm->entry->index, fsm->entry->subindex,
+ fsm->entry->bit_length, fsm->entry_pos);
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_entry_print(fsm);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ // find next entry
+ if (!(fsm->entry = ec_fsm_pdo_entry_conf_next_entry(
+ fsm, &fsm->entry->list))) {
+
+ // No more entries to add. Write entry count.
+ EC_WRITE_U8(fsm->request.data, fsm->entry_pos);
+ fsm->request.data_size = 1;
+ ecrt_sdo_request_index(&fsm->request, fsm->source_pdo->index, 0);
+ ecrt_sdo_request_write(&fsm->request);
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Setting number of PDO entries to %u.\n",
+ fsm->entry_pos);
+
+ fsm->state = ec_fsm_pdo_entry_conf_state_set_entry_count;
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request);
+ ec_fsm_coe_exec(fsm->fsm_coe, datagram); // execute immediately
+ return;
+ }
+
+ // add next entry
+ fsm->entry_pos++;
+ ec_fsm_pdo_entry_conf_action_map(fsm, datagram);
+}
+
+/*****************************************************************************/
+
+/** Set the number of entries.
+ */
+void ec_fsm_pdo_entry_conf_state_set_entry_count(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_WARN(fsm->slave, "Failed to set number of entries.\n");
+ EC_SLAVE_WARN(fsm->slave, ""); ec_fsm_pdo_entry_print(fsm);
+ fsm->state = ec_fsm_pdo_entry_state_error;
+ return;
+ }
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Successfully configured"
+ " mapping for PDO 0x%04X.\n", fsm->source_pdo->index);
+
+ fsm->state = ec_fsm_pdo_entry_state_end; // finished
+}
+
+/******************************************************************************
+ * Common state functions
+ *****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_pdo_entry_state_error(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_pdo_entry_state_end(
+ ec_fsm_pdo_entry_t *fsm, /**< PDO mapping state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_pdo_entry.h b/net/ethercat/master/fsm_pdo_entry.h
new file mode 100644
index 000000000000..af15e3af88f2
--- /dev/null
+++ b/net/ethercat/master/fsm_pdo_entry.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO entry configuration state machine structures.
+ */
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_PDO_ENTRY_H__
+#define __EC_FSM_PDO_ENTRY_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "fsm_coe.h"
+
+/*****************************************************************************/
+
+/**
+ * \see ec_fsm_pdo_entry
+ */
+typedef struct ec_fsm_pdo_entry ec_fsm_pdo_entry_t;
+
+/** PDO configuration state machine.
+ */
+struct ec_fsm_pdo_entry
+{
+ void (*state)(ec_fsm_pdo_entry_t *, ec_datagram_t *); /**< state function
+ */
+ ec_fsm_coe_t *fsm_coe; /**< CoE state machine to use */
+ ec_sdo_request_t request; /**< SDO request. */
+
+ ec_slave_t *slave; /**< Slave the FSM runs on. */
+ ec_pdo_t *target_pdo; /**< PDO to read the mapping for. */
+ const ec_pdo_t *source_pdo; /**< PDO with desired mapping. */
+ const ec_pdo_t *cur_pdo; /**< PDO with current mapping (display only). */
+ const ec_pdo_entry_t *entry; /**< Current entry. */
+ unsigned int entry_count; /**< Number of entries. */
+ unsigned int entry_pos; /**< Position in PDO mapping. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_pdo_entry_init(ec_fsm_pdo_entry_t *, ec_fsm_coe_t *);
+void ec_fsm_pdo_entry_clear(ec_fsm_pdo_entry_t *);
+
+void ec_fsm_pdo_entry_start_reading(ec_fsm_pdo_entry_t *, ec_slave_t *,
+ ec_pdo_t *);
+void ec_fsm_pdo_entry_start_configuration(ec_fsm_pdo_entry_t *, ec_slave_t *,
+ const ec_pdo_t *, const ec_pdo_t *);
+
+int ec_fsm_pdo_entry_exec(ec_fsm_pdo_entry_t *, ec_datagram_t *);
+int ec_fsm_pdo_entry_success(const ec_fsm_pdo_entry_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_sii.c b/net/ethercat/master/fsm_sii.c
new file mode 100644
index 000000000000..fb911b38fecf
--- /dev/null
+++ b/net/ethercat/master/fsm_sii.c
@@ -0,0 +1,490 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave information interface FSM.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "mailbox.h"
+#include "master.h"
+#include "fsm_sii.h"
+
+/** Read/write timeout [ms].
+ *
+ * Used to calculate timeouts bsed on the jiffies counter.
+ *
+ * \attention Must be more than 10 to avoid problems on kernels that run with
+ * a timer interupt frequency of 100 Hz.
+ */
+#define SII_TIMEOUT 20
+
+/** Time before evaluating answer at writing [ms].
+ */
+#define SII_INHIBIT 5
+
+//#define SII_DEBUG
+
+/*****************************************************************************/
+
+void ec_fsm_sii_state_start_reading(ec_fsm_sii_t *);
+void ec_fsm_sii_state_read_check(ec_fsm_sii_t *);
+void ec_fsm_sii_state_read_fetch(ec_fsm_sii_t *);
+void ec_fsm_sii_state_start_writing(ec_fsm_sii_t *);
+void ec_fsm_sii_state_write_check(ec_fsm_sii_t *);
+void ec_fsm_sii_state_write_check2(ec_fsm_sii_t *);
+void ec_fsm_sii_state_end(ec_fsm_sii_t *);
+void ec_fsm_sii_state_error(ec_fsm_sii_t *);
+
+/*****************************************************************************/
+
+/**
+ Constructor.
+*/
+
+void ec_fsm_sii_init(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< datagram structure to use */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = datagram;
+}
+
+/*****************************************************************************/
+
+/**
+ Destructor.
+*/
+
+void ec_fsm_sii_clear(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+}
+
+/*****************************************************************************/
+
+/**
+ Initializes the SII read state machine.
+*/
+
+void ec_fsm_sii_read(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< slave to read from */
+ uint16_t word_offset, /**< offset to read from */
+ ec_fsm_sii_addressing_t mode /**< addressing scheme */
+ )
+{
+ fsm->state = ec_fsm_sii_state_start_reading;
+ fsm->slave = slave;
+ fsm->word_offset = word_offset;
+ fsm->mode = mode;
+}
+
+/*****************************************************************************/
+
+/**
+ Initializes the SII write state machine.
+*/
+
+void ec_fsm_sii_write(ec_fsm_sii_t *fsm, /**< finite state machine */
+ ec_slave_t *slave, /**< slave to read from */
+ uint16_t word_offset, /**< offset to read from */
+ const uint16_t *value, /**< pointer to 2 bytes of data */
+ ec_fsm_sii_addressing_t mode /**< addressing scheme */
+ )
+{
+ fsm->state = ec_fsm_sii_state_start_writing;
+ fsm->slave = slave;
+ fsm->word_offset = word_offset;
+ fsm->mode = mode;
+ memcpy(fsm->value, value, 2);
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the SII state machine.
+ \return false, if the state machine has terminated
+*/
+
+int ec_fsm_sii_exec(ec_fsm_sii_t *fsm /**< finite state machine */)
+{
+ fsm->state(fsm);
+
+ return fsm->state != ec_fsm_sii_state_end
+ && fsm->state != ec_fsm_sii_state_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Returns, if the master startup state machine terminated with success.
+ \return non-zero if successful.
+*/
+
+int ec_fsm_sii_success(ec_fsm_sii_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_sii_state_end;
+}
+
+/******************************************************************************
+ * state functions
+ *****************************************************************************/
+
+/**
+ SII state: START READING.
+ Starts reading the slave information interface.
+*/
+
+void ec_fsm_sii_state_start_reading(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ // initiate read operation
+ switch (fsm->mode) {
+ case EC_FSM_SII_USE_INCREMENT_ADDRESS:
+ ec_datagram_apwr(datagram, fsm->slave->ring_position, 0x502, 4);
+ break;
+ case EC_FSM_SII_USE_CONFIGURED_ADDRESS:
+ ec_datagram_fpwr(datagram, fsm->slave->station_address, 0x502, 4);
+ break;
+ }
+
+ EC_WRITE_U8 (datagram->data, 0x80); // two address octets
+ EC_WRITE_U8 (datagram->data + 1, 0x01); // request read operation
+ EC_WRITE_U16(datagram->data + 2, fsm->word_offset);
+
+#ifdef SII_DEBUG
+ EC_SLAVE_DBG(fsm->slave, 0, "reading SII data, word %u:\n",
+ fsm->word_offset);
+ ec_print_data(datagram->data, 4);
+#endif
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_sii_state_read_check;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: READ CHECK.
+ Checks, if the SII-read-datagram has been sent and issues a fetch datagram.
+*/
+
+void ec_fsm_sii_state_read_check(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed to receive SII read datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Reception of SII read datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ fsm->jiffies_start = datagram->jiffies_sent;
+ fsm->check_once_more = 1;
+
+ // issue check/fetch datagram
+ switch (fsm->mode) {
+ case EC_FSM_SII_USE_INCREMENT_ADDRESS:
+ ec_datagram_aprd(datagram, fsm->slave->ring_position, 0x502, 10);
+ break;
+ case EC_FSM_SII_USE_CONFIGURED_ADDRESS:
+ ec_datagram_fprd(datagram, fsm->slave->station_address, 0x502, 10);
+ break;
+ }
+
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_sii_state_read_fetch;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: READ FETCH.
+ Fetches the result of an SII-read datagram.
+*/
+void ec_fsm_sii_state_read_fetch(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Failed to receive SII check/fetch datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Reception of SII check/fetch datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+#ifdef SII_DEBUG
+ EC_SLAVE_DBG(fsm->slave, 0, "checking SII read state:\n");
+ ec_print_data(datagram->data, 10);
+#endif
+
+ if (EC_READ_U8(datagram->data + 1) & 0x20) {
+ EC_SLAVE_ERR(fsm->slave, "Error on last command while"
+ " reading from SII word 0x%04x.\n", fsm->word_offset);
+ fsm->state = ec_fsm_sii_state_error;
+ return;
+ }
+
+ // check "busy bit"
+ if (EC_READ_U8(datagram->data + 1) & 0x81) { /* busy bit or
+ read operation busy */
+ // still busy... timeout?
+ unsigned long diff_ms =
+ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
+ if (diff_ms >= SII_TIMEOUT) {
+ if (fsm->check_once_more) {
+ fsm->check_once_more = 0;
+ } else {
+ EC_SLAVE_ERR(fsm->slave, "SII: Read timeout.\n");
+ fsm->state = ec_fsm_sii_state_error;
+ return;
+ }
+ }
+
+ // issue check/fetch datagram again
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+
+ // SII value received.
+ memcpy(fsm->value, datagram->data + 6, 4);
+ fsm->state = ec_fsm_sii_state_end;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: START WRITING.
+ Starts writing a word through the slave information interface.
+*/
+
+void ec_fsm_sii_state_start_writing(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ // initiate write operation
+ ec_datagram_fpwr(datagram, fsm->slave->station_address, 0x502, 8);
+ EC_WRITE_U8 (datagram->data, 0x81); /* two address octets
+ + enable write access */
+ EC_WRITE_U8 (datagram->data + 1, 0x02); // request write operation
+ EC_WRITE_U16(datagram->data + 2, fsm->word_offset);
+ memset(datagram->data + 4, 0x00, 2);
+ memcpy(datagram->data + 6, fsm->value, 2);
+
+#ifdef SII_DEBUG
+ EC_SLAVE_DBG(fsm->slave, 0, "writing SII data:\n");
+ ec_print_data(datagram->data, 8);
+#endif
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_sii_state_write_check;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: WRITE CHECK.
+*/
+
+void ec_fsm_sii_state_write_check(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed to receive SII write datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Reception of SII write datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ fsm->jiffies_start = datagram->jiffies_sent;
+ fsm->check_once_more = 1;
+
+ // issue check datagram
+ ec_datagram_fprd(datagram, fsm->slave->station_address, 0x502, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_sii_state_write_check2;
+}
+
+/*****************************************************************************/
+
+/**
+ SII state: WRITE CHECK 2.
+*/
+
+void ec_fsm_sii_state_write_check2(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ unsigned long diff_ms;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Failed to receive SII write check datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->state = ec_fsm_sii_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Reception of SII write check datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+#ifdef SII_DEBUG
+ EC_SLAVE_DBG(fsm->slave, 0, "checking SII write state:\n");
+ ec_print_data(datagram->data, 2);
+#endif
+
+ if (EC_READ_U8(datagram->data + 1) & 0x20) {
+ EC_SLAVE_ERR(fsm->slave, "SII: Error on last SII command!\n");
+ fsm->state = ec_fsm_sii_state_error;
+ return;
+ }
+
+ /* FIXME: some slaves never answer with the busy flag set...
+ * wait a few ms for the write operation to complete. */
+ diff_ms = (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
+ if (diff_ms < SII_INHIBIT) {
+#ifdef SII_DEBUG
+ EC_SLAVE_DBG(fsm->slave, 0, "too early.\n");
+#endif
+ // issue check datagram again
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+
+ if (EC_READ_U8(datagram->data + 1) & 0x82) { /* busy bit or
+ write operation busy bit */
+ // still busy... timeout?
+ if (diff_ms >= SII_TIMEOUT) {
+ if (fsm->check_once_more) {
+ fsm->check_once_more = 0;
+ } else {
+ EC_SLAVE_ERR(fsm->slave, "SII: Write timeout.\n");
+ fsm->state = ec_fsm_sii_state_error;
+ return;
+ }
+ }
+
+ // issue check datagram again
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+
+ if (EC_READ_U8(datagram->data + 1) & 0x40) {
+ EC_SLAVE_ERR(fsm->slave, "SII: Write operation failed!\n");
+ fsm->state = ec_fsm_sii_state_error;
+ return;
+ }
+
+ // success
+ fsm->state = ec_fsm_sii_state_end;
+}
+
+/*****************************************************************************/
+
+/**
+ State: ERROR.
+*/
+
+void ec_fsm_sii_state_error(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/**
+ State: END.
+*/
+
+void ec_fsm_sii_state_end(
+ ec_fsm_sii_t *fsm /**< finite state machine */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_sii.h b/net/ethercat/master/fsm_sii.h
new file mode 100644
index 000000000000..eefac5e7a3e6
--- /dev/null
+++ b/net/ethercat/master/fsm_sii.h
@@ -0,0 +1,90 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave information interface FSM structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SII_H__
+#define __EC_FSM_SII_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "slave.h"
+
+/*****************************************************************************/
+
+/** SII access addressing mode.
+ */
+typedef enum {
+ EC_FSM_SII_USE_INCREMENT_ADDRESS, /**< Use auto-increment addressing. */
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS /**< Use configured addresses. */
+} ec_fsm_sii_addressing_t;
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_sii ec_fsm_sii_t; /**< \see ec_fsm_sii */
+
+/**
+ Slave information interface FSM.
+*/
+
+struct ec_fsm_sii
+{
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ ec_datagram_t *datagram; /**< datagram used in the state machine */
+ unsigned int retries; /**< retries upon datagram timeout */
+
+ void (*state)(ec_fsm_sii_t *); /**< SII state function */
+ uint16_t word_offset; /**< input: word offset in SII */
+ ec_fsm_sii_addressing_t mode; /**< reading via APRD or NPRD */
+ uint8_t value[4]; /**< raw SII value (32bit) */
+ unsigned long jiffies_start; /**< Start timestamp. */
+ uint8_t check_once_more; /**< one more try after timeout */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_sii_init(ec_fsm_sii_t *, ec_datagram_t *);
+void ec_fsm_sii_clear(ec_fsm_sii_t *);
+
+void ec_fsm_sii_read(ec_fsm_sii_t *, ec_slave_t *,
+ uint16_t, ec_fsm_sii_addressing_t);
+void ec_fsm_sii_write(ec_fsm_sii_t *, ec_slave_t *, uint16_t,
+ const uint16_t *, ec_fsm_sii_addressing_t);
+
+int ec_fsm_sii_exec(ec_fsm_sii_t *);
+int ec_fsm_sii_success(ec_fsm_sii_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_slave.c b/net/ethercat/master/fsm_slave.c
new file mode 100644
index 000000000000..0ddb76f7e817
--- /dev/null
+++ b/net/ethercat/master/fsm_slave.c
@@ -0,0 +1,582 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 slave (SDO) state machine.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+
+#include "fsm_slave.h"
+
+/*****************************************************************************/
+
+void ec_fsm_slave_state_idle(ec_fsm_slave_t *, ec_datagram_t *);
+void ec_fsm_slave_state_ready(ec_fsm_slave_t *, ec_datagram_t *);
+int ec_fsm_slave_action_process_sdo(ec_fsm_slave_t *, ec_datagram_t *);
+void ec_fsm_slave_state_sdo_request(ec_fsm_slave_t *, ec_datagram_t *);
+int ec_fsm_slave_action_process_reg(ec_fsm_slave_t *, ec_datagram_t *);
+void ec_fsm_slave_state_reg_request(ec_fsm_slave_t *, ec_datagram_t *);
+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 *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_slave_init(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ fsm->slave = slave;
+ INIT_LIST_HEAD(&fsm->list); // mark as unlisted
+
+ fsm->state = ec_fsm_slave_state_idle;
+ fsm->datagram = NULL;
+ fsm->sdo_request = NULL;
+ fsm->reg_request = NULL;
+ fsm->foe_request = NULL;
+ fsm->soe_request = NULL;
+
+ // 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);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_slave_clear(
+ ec_fsm_slave_t *fsm /**< Master state machine. */
+ )
+{
+ // signal requests that are currently in operation
+
+ if (fsm->sdo_request) {
+ fsm->sdo_request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&fsm->slave->master->request_queue);
+ }
+
+ if (fsm->reg_request) {
+ fsm->reg_request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&fsm->slave->master->request_queue);
+ }
+
+ if (fsm->foe_request) {
+ fsm->foe_request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&fsm->slave->master->request_queue);
+ }
+
+ if (fsm->soe_request) {
+ fsm->soe_request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&fsm->slave->master->request_queue);
+ }
+
+ // 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);
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * \return 1 if \a datagram was used, else 0.
+ */
+int ec_fsm_slave_exec(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< New datagram to use. */
+ )
+{
+ int datagram_used;
+
+ fsm->state(fsm, datagram);
+
+ datagram_used = fsm->state != ec_fsm_slave_state_idle &&
+ fsm->state != ec_fsm_slave_state_ready;
+
+ if (datagram_used) {
+ fsm->datagram = datagram;
+ } else {
+ fsm->datagram = NULL;
+ }
+
+ return datagram_used;
+}
+
+/*****************************************************************************/
+
+/** Sets the current state of the state machine to READY
+ */
+void ec_fsm_slave_set_ready(
+ ec_fsm_slave_t *fsm /**< Slave state machine. */
+ )
+{
+ if (fsm->state == ec_fsm_slave_state_idle) {
+ EC_SLAVE_DBG(fsm->slave, 1, "Ready for requests.\n");
+ fsm->state = ec_fsm_slave_state_ready;
+ }
+}
+
+/*****************************************************************************/
+
+/** Returns, if the FSM is currently not busy and ready to execute.
+ *
+ * \return Non-zero if ready.
+ */
+int ec_fsm_slave_is_ready(
+ const ec_fsm_slave_t *fsm /**< Slave state machine. */
+ )
+{
+ return fsm->state == ec_fsm_slave_state_ready;
+}
+
+/******************************************************************************
+ * Slave state machine
+ *****************************************************************************/
+
+/** Slave state: IDLE.
+ */
+void ec_fsm_slave_state_idle(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ // do nothing
+}
+
+/*****************************************************************************/
+
+/** Slave state: READY.
+ */
+void ec_fsm_slave_state_ready(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ // Check for pending external SDO requests
+ if (ec_fsm_slave_action_process_sdo(fsm, datagram)) {
+ return;
+ }
+
+ // Check for pending external register requests
+ if (ec_fsm_slave_action_process_reg(fsm, datagram)) {
+ return;
+ }
+
+ // Check for pending FoE requests
+ if (ec_fsm_slave_action_process_foe(fsm, datagram)) {
+ return;
+ }
+
+ // Check for pending SoE requests
+ if (ec_fsm_slave_action_process_soe(fsm, datagram)) {
+ return;
+ }
+}
+
+/*****************************************************************************/
+
+/** Check for pending SDO requests and process one.
+ *
+ * \return non-zero, if an SDO request is processed.
+ */
+int ec_fsm_slave_action_process_sdo(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request;
+
+ if (list_empty(&slave->sdo_requests)) {
+ return 0;
+ }
+
+ // take the first request to be processed
+ request = list_entry(slave->sdo_requests.next, ec_sdo_request_t, list);
+ list_del_init(&request->list); // dequeue
+
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ EC_SLAVE_WARN(slave, "Aborting SDO 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 1;
+ }
+
+ if (slave->current_state == EC_SLAVE_STATE_INIT) {
+ EC_SLAVE_WARN(slave, "Aborting SDO 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 1;
+ }
+
+ fsm->sdo_request = request;
+ request->state = EC_INT_REQUEST_BUSY;
+
+ // Found pending SDO request. Execute it!
+ EC_SLAVE_DBG(slave, 1, "Processing SDO request...\n");
+
+ // Start SDO transfer
+ fsm->state = ec_fsm_slave_state_sdo_request;
+ ec_fsm_coe_transfer(&fsm->fsm_coe, slave, request);
+ ec_fsm_coe_exec(&fsm->fsm_coe, datagram); // execute immediately
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Slave state: SDO_REQUEST.
+ */
+void ec_fsm_slave_state_sdo_request(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_sdo_request_t *request = fsm->sdo_request;
+
+ if (ec_fsm_coe_exec(&fsm->fsm_coe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
+ EC_SLAVE_ERR(slave, "Failed to process SDO request.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->sdo_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Finished SDO request.\n");
+
+ // SDO request finished
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&slave->master->request_queue);
+ fsm->sdo_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+}
+
+/*****************************************************************************/
+
+/** Check for pending register requests and process one.
+ *
+ * \return non-zero, if a register request is processed.
+ */
+int ec_fsm_slave_action_process_reg(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_reg_request_t *reg;
+
+ fsm->reg_request = NULL;
+
+ if (slave->config) {
+ // search the first internal register request to be processed
+ list_for_each_entry(reg, &slave->config->reg_requests, list) {
+ if (reg->state == EC_INT_REQUEST_QUEUED) {
+ fsm->reg_request = reg;
+ break;
+ }
+ }
+ }
+
+ if (!fsm->reg_request && !list_empty(&slave->reg_requests)) {
+ // take the first external request to be processed
+ fsm->reg_request =
+ list_entry(slave->reg_requests.next, ec_reg_request_t, list);
+ list_del_init(&fsm->reg_request->list); // dequeue
+ }
+
+ if (!fsm->reg_request) { // no register request to process
+ return 0;
+ }
+
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ EC_SLAVE_WARN(slave, "Aborting register request,"
+ " slave has error flag set.\n");
+ fsm->reg_request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->reg_request = NULL;
+ fsm->state = ec_fsm_slave_state_idle;
+ return 1;
+ }
+
+ // Found pending register request. Execute it!
+ EC_SLAVE_DBG(slave, 1, "Processing register request...\n");
+
+ fsm->reg_request->state = EC_INT_REQUEST_BUSY;
+
+ // Start register access
+ if (fsm->reg_request->dir == EC_DIR_INPUT) {
+ ec_datagram_fprd(datagram, slave->station_address,
+ fsm->reg_request->address, fsm->reg_request->transfer_size);
+ ec_datagram_zero(datagram);
+ } else {
+ ec_datagram_fpwr(datagram, slave->station_address,
+ fsm->reg_request->address, fsm->reg_request->transfer_size);
+ memcpy(datagram->data, fsm->reg_request->data,
+ fsm->reg_request->transfer_size);
+ }
+ datagram->device_index = slave->device_index;
+ fsm->state = ec_fsm_slave_state_reg_request;
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Slave state: Register request.
+ */
+void ec_fsm_slave_state_reg_request(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_reg_request_t *reg = fsm->reg_request;
+
+ if (!reg) {
+ // configuration was cleared in the meantime
+ fsm->state = ec_fsm_slave_state_ready;
+ fsm->reg_request = NULL;
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_ERR(slave, "Failed to receive register"
+ " request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ reg->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->reg_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+ return;
+ }
+
+ if (fsm->datagram->working_counter == 1) {
+ if (reg->dir == EC_DIR_INPUT) { // read request
+ memcpy(reg->data, fsm->datagram->data, reg->transfer_size);
+ }
+
+ reg->state = EC_INT_REQUEST_SUCCESS;
+ EC_SLAVE_DBG(slave, 1, "Register request successful.\n");
+ } else {
+ reg->state = EC_INT_REQUEST_FAILURE;
+ ec_datagram_print_state(fsm->datagram);
+ EC_SLAVE_ERR(slave, "Register request failed"
+ " (working counter is %u).\n",
+ fsm->datagram->working_counter);
+ }
+
+ wake_up_all(&slave->master->request_queue);
+ fsm->reg_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+}
+
+/*****************************************************************************/
+
+/** Check for pending FoE requests and process one.
+ *
+ * \return non-zero, if an FoE request is processed.
+ */
+int ec_fsm_slave_action_process_foe(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_foe_request_t *request;
+
+ if (list_empty(&slave->foe_requests)) {
+ return 0;
+ }
+
+ // take the first request to be processed
+ request = list_entry(slave->foe_requests.next, ec_foe_request_t, list);
+ list_del_init(&request->list); // dequeue
+
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ EC_SLAVE_WARN(slave, "Aborting FoE 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 1;
+ }
+
+ request->state = EC_INT_REQUEST_BUSY;
+ fsm->foe_request = request;
+
+ EC_SLAVE_DBG(slave, 1, "Processing FoE request.\n");
+
+ fsm->state = ec_fsm_slave_state_foe_request;
+ ec_fsm_foe_transfer(&fsm->fsm_foe, slave, request);
+ ec_fsm_foe_exec(&fsm->fsm_foe, datagram);
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Slave state: FOE REQUEST.
+ */
+void ec_fsm_slave_state_foe_request(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_foe_request_t *request = fsm->foe_request;
+
+ if (ec_fsm_foe_exec(&fsm->fsm_foe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_foe_success(&fsm->fsm_foe)) {
+ EC_SLAVE_ERR(slave, "Failed to handle FoE request.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->foe_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+ return;
+ }
+
+ // finished transferring FoE
+ EC_SLAVE_DBG(slave, 1, "Successfully transferred %zu bytes of FoE"
+ " data.\n", request->data_size);
+
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&slave->master->request_queue);
+ fsm->foe_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+}
+
+/*****************************************************************************/
+
+/** Check for pending SoE requests and process one.
+ *
+ * \return non-zero, if a request is processed.
+ */
+int ec_fsm_slave_action_process_soe(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *req;
+
+ if (list_empty(&slave->soe_requests)) {
+ return 0;
+ }
+
+ // take the first request to be processed
+ req = list_entry(slave->soe_requests.next, ec_soe_request_t, list);
+ list_del_init(&req->list); // dequeue
+
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ EC_SLAVE_WARN(slave, "Aborting SoE request,"
+ " slave has error flag set.\n");
+ req->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->state = ec_fsm_slave_state_idle;
+ return 1;
+ }
+
+ if (slave->current_state == EC_SLAVE_STATE_INIT) {
+ EC_SLAVE_WARN(slave, "Aborting SoE request, slave is in INIT.\n");
+ req->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->state = ec_fsm_slave_state_idle;
+ return 0;
+ }
+
+ fsm->soe_request = req;
+ req->state = EC_INT_REQUEST_BUSY;
+
+ // Found pending request. Execute it!
+ EC_SLAVE_DBG(slave, 1, "Processing SoE request...\n");
+
+ // Start SoE transfer
+ fsm->state = ec_fsm_slave_state_soe_request;
+ ec_fsm_soe_transfer(&fsm->fsm_soe, slave, req);
+ ec_fsm_soe_exec(&fsm->fsm_soe, datagram); // execute immediately
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Slave state: SOE_REQUEST.
+ */
+void ec_fsm_slave_state_soe_request(
+ ec_fsm_slave_t *fsm, /**< Slave state machine. */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *request = fsm->soe_request;
+
+ if (ec_fsm_soe_exec(&fsm->fsm_soe, datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_soe_success(&fsm->fsm_soe)) {
+ EC_SLAVE_ERR(slave, "Failed to process SoE request.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&slave->master->request_queue);
+ fsm->soe_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Finished SoE request.\n");
+
+ // SoE request finished
+ request->state = EC_INT_REQUEST_SUCCESS;
+ wake_up_all(&slave->master->request_queue);
+ fsm->soe_request = NULL;
+ fsm->state = ec_fsm_slave_state_ready;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_slave.h b/net/ethercat/master/fsm_slave.h
new file mode 100644
index 000000000000..b511a6683392
--- /dev/null
+++ b/net/ethercat/master/fsm_slave.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 slave request state machine.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SLAVE_H__
+#define __EC_FSM_SLAVE_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "sdo_request.h"
+#include "reg_request.h"
+#include "fsm_coe.h"
+#include "fsm_foe.h"
+#include "fsm_soe.h"
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_slave ec_fsm_slave_t; /**< \see ec_fsm_slave */
+
+/** Finite state machine of an EtherCAT slave.
+ */
+struct ec_fsm_slave {
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ struct list_head list; /**< Used for execution list. */
+
+ void (*state)(ec_fsm_slave_t *, ec_datagram_t *); /**< State function. */
+ ec_datagram_t *datagram; /**< Previous state datagram. */
+ ec_sdo_request_t *sdo_request; /**< SDO request to process. */
+ ec_reg_request_t *reg_request; /**< Register request to process. */
+ 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. */
+
+ 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. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_slave_init(ec_fsm_slave_t *, ec_slave_t *);
+void ec_fsm_slave_clear(ec_fsm_slave_t *);
+
+int ec_fsm_slave_exec(ec_fsm_slave_t *, ec_datagram_t *);
+void ec_fsm_slave_set_ready(ec_fsm_slave_t *);
+int ec_fsm_slave_is_ready(const ec_fsm_slave_t *);
+
+/*****************************************************************************/
+
+
+#endif // __EC_FSM_SLAVE_H__
diff --git a/net/ethercat/master/fsm_slave_config.c b/net/ethercat/master/fsm_slave_config.c
new file mode 100644
index 000000000000..25ffdd1d5617
--- /dev/null
+++ b/net/ethercat/master/fsm_slave_config.c
@@ -0,0 +1,1730 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave configuration state machine.
+ */
+
+/*****************************************************************************/
+
+#include <asm/div64.h>
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+#include "fsm_slave_config.h"
+
+/*****************************************************************************/
+
+/** Maximum clock difference (in ns) before going to SAFEOP.
+ *
+ * Wait for DC time difference to drop under this absolute value before
+ * requesting SAFEOP.
+ */
+#define EC_DC_MAX_SYNC_DIFF_NS 10000
+
+/** Maximum time (in ms) to wait for clock discipline.
+ */
+#define EC_DC_SYNC_WAIT_MS 5000
+
+/** Time offset (in ns), that is added to cyclic start time.
+ */
+#define EC_DC_START_OFFSET 100000000ULL
+
+/*****************************************************************************/
+
+void ec_fsm_slave_config_state_start(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_init(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_clear_fmmus(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_clear_sync(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_dc_clear_assign(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_mbox_sync(ec_fsm_slave_config_t *);
+#ifdef EC_SII_ASSIGN
+void ec_fsm_slave_config_state_assign_pdi(ec_fsm_slave_config_t *);
+#endif
+void ec_fsm_slave_config_state_boot_preop(ec_fsm_slave_config_t *);
+#ifdef EC_SII_ASSIGN
+void ec_fsm_slave_config_state_assign_ethercat(ec_fsm_slave_config_t *);
+#endif
+void ec_fsm_slave_config_state_sdo_conf(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_soe_conf_preop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_watchdog_divider(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_watchdog(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_pdo_sync(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_pdo_conf(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_fmmu(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_dc_cycle(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_dc_sync_check(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_dc_start(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_dc_assign(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_safeop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_soe_conf_safeop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_op(ec_fsm_slave_config_t *);
+
+void ec_fsm_slave_config_enter_init(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_clear_sync(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_dc_clear_assign(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_mbox_sync(ec_fsm_slave_config_t *);
+#ifdef EC_SII_ASSIGN
+void ec_fsm_slave_config_enter_assign_pdi(ec_fsm_slave_config_t *);
+#endif
+void ec_fsm_slave_config_enter_boot_preop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_sdo_conf(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_soe_conf_preop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_pdo_conf(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_watchdog_divider(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_watchdog(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_pdo_sync(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_fmmu(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_dc_cycle(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_safeop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_soe_conf_safeop(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_enter_op(ec_fsm_slave_config_t *);
+
+void ec_fsm_slave_config_state_end(ec_fsm_slave_config_t *);
+void ec_fsm_slave_config_state_error(ec_fsm_slave_config_t *);
+
+void ec_fsm_slave_config_reconfigure(ec_fsm_slave_config_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_slave_config_init(
+ ec_fsm_slave_config_t *fsm, /**< slave state machine */
+ ec_datagram_t *datagram, /**< datagram structure to use */
+ ec_fsm_change_t *fsm_change, /**< State change state machine to use. */
+ ec_fsm_coe_t *fsm_coe, /**< CoE state machine to use. */
+ ec_fsm_soe_t *fsm_soe, /**< SoE state machine to use. */
+ ec_fsm_pdo_t *fsm_pdo /**< PDO configuration state machine to use. */
+ )
+{
+ ec_sdo_request_init(&fsm->request_copy);
+ ec_soe_request_init(&fsm->soe_request_copy);
+
+ fsm->datagram = datagram;
+ fsm->fsm_change = fsm_change;
+ fsm->fsm_coe = fsm_coe;
+ fsm->fsm_soe = fsm_soe;
+ fsm->fsm_pdo = fsm_pdo;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_slave_config_clear(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_sdo_request_clear(&fsm->request_copy);
+ ec_soe_request_clear(&fsm->soe_request_copy);
+}
+
+/*****************************************************************************/
+
+/** Start slave configuration state machine.
+ */
+void ec_fsm_slave_config_start(
+ ec_fsm_slave_config_t *fsm, /**< slave state machine */
+ ec_slave_t *slave /**< slave to configure */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_slave_config_state_start;
+}
+
+/*****************************************************************************/
+
+/**
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_slave_config_running(
+ const ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ return fsm->state != ec_fsm_slave_config_state_end
+ && fsm->state != ec_fsm_slave_config_state_error;
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * If the state machine's datagram is not sent or received yet, the execution
+ * of the state machine is delayed to the next cycle.
+ *
+ * \return false, if state machine has terminated
+ */
+int ec_fsm_slave_config_exec(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ if (fsm->datagram->state == EC_DATAGRAM_SENT
+ || fsm->datagram->state == EC_DATAGRAM_QUEUED) {
+ // datagram was not sent or received yet.
+ return ec_fsm_slave_config_running(fsm);
+ }
+
+ fsm->state(fsm);
+ return ec_fsm_slave_config_running(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ * \return true, if the state machine terminated gracefully
+ */
+int ec_fsm_slave_config_success(
+ const ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ return fsm->state == ec_fsm_slave_config_state_end;
+}
+
+/******************************************************************************
+ * Slave configuration state machine
+ *****************************************************************************/
+
+/** Slave configuration state: START.
+ */
+void ec_fsm_slave_config_state_start(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ EC_SLAVE_DBG(fsm->slave, 1, "Configuring...\n");
+ ec_fsm_slave_config_enter_init(fsm);
+}
+
+/*****************************************************************************/
+
+/** Start state change to INIT.
+ */
+void ec_fsm_slave_config_enter_init(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_fsm_change_start(fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_INIT);
+ ec_fsm_change_exec(fsm->fsm_change);
+ fsm->state = ec_fsm_slave_config_state_init;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: INIT.
+ */
+void ec_fsm_slave_config_state_init(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (ec_fsm_change_exec(fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(fsm->fsm_change)) {
+ if (!fsm->fsm_change->spontaneous_change)
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Now in INIT.\n");
+
+ if (!slave->base_fmmu_count) { // skip FMMU configuration
+ ec_fsm_slave_config_enter_clear_sync(fsm);
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Clearing FMMU configurations...\n");
+
+ // clear FMMU configurations
+ ec_datagram_fpwr(datagram, slave->station_address,
+ 0x0600, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_clear_fmmus;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: CLEAR FMMU.
+ */
+void ec_fsm_slave_config_state_clear_fmmus(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed receive FMMU clearing datagram.\n");
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed to clear FMMUs: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_clear_sync(fsm);
+}
+
+/*****************************************************************************/
+
+/** Clear the sync manager configurations.
+ */
+void ec_fsm_slave_config_enter_clear_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+ size_t sync_size;
+
+ if (!slave->base_sync_count) {
+ // no sync managers
+ ec_fsm_slave_config_enter_dc_clear_assign(fsm);
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Clearing sync manager configurations...\n");
+
+ sync_size = EC_SYNC_PAGE_SIZE * slave->base_sync_count;
+
+ // clear sync manager configurations
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0800, sync_size);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_clear_sync;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: CLEAR SYNC.
+ */
+void ec_fsm_slave_config_state_clear_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed receive sync manager"
+ " clearing datagram.\n");
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Failed to clear sync manager configurations: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_dc_clear_assign(fsm);
+}
+
+/*****************************************************************************/
+
+/** Clear the DC assignment.
+ */
+void ec_fsm_slave_config_enter_dc_clear_assign(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (!slave->base_dc_supported || !slave->has_dc_system_time) {
+ ec_fsm_slave_config_enter_mbox_sync(fsm);
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Clearing DC assignment...\n");
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0980, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_dc_clear_assign;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: CLEAR DC ASSIGN.
+ */
+void ec_fsm_slave_config_state_dc_clear_assign(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed receive DC assignment"
+ " clearing datagram.\n");
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ // clearing the DC assignment does not succeed on simple slaves
+ EC_SLAVE_DBG(fsm->slave, 1, "Failed to clear DC assignment: ");
+ ec_datagram_print_wc_error(datagram);
+ }
+
+ ec_fsm_slave_config_enter_mbox_sync(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for mailbox sync managers to be configured.
+ */
+void ec_fsm_slave_config_enter_mbox_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+ unsigned int i;
+
+ // slave is now in INIT
+ if (slave->current_state == slave->requested_state) {
+ fsm->state = ec_fsm_slave_config_state_end; // successful
+ EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
+ return;
+ }
+
+ if (!slave->sii.mailbox_protocols) {
+ // no mailbox protocols supported
+ EC_SLAVE_DBG(slave, 1, "Slave does not support"
+ " mailbox communication.\n");
+#ifdef EC_SII_ASSIGN
+ ec_fsm_slave_config_enter_assign_pdi(fsm);
+#else
+ ec_fsm_slave_config_enter_boot_preop(fsm);
+#endif
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Configuring mailbox sync managers...\n");
+
+ if (slave->requested_state == EC_SLAVE_STATE_BOOT) {
+ ec_sync_t sync;
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
+ EC_SYNC_PAGE_SIZE * 2);
+ ec_datagram_zero(datagram);
+
+ ec_sync_init(&sync, slave);
+ sync.physical_start_address = slave->sii.boot_rx_mailbox_offset;
+ sync.control_register = 0x26;
+ sync.enable = 1;
+ ec_sync_page(&sync, 0, slave->sii.boot_rx_mailbox_size,
+ EC_DIR_INVALID, // use default direction
+ 0, // no PDO xfer
+ datagram->data);
+ slave->configured_rx_mailbox_offset =
+ slave->sii.boot_rx_mailbox_offset;
+ slave->configured_rx_mailbox_size =
+ slave->sii.boot_rx_mailbox_size;
+
+ ec_sync_init(&sync, slave);
+ sync.physical_start_address = slave->sii.boot_tx_mailbox_offset;
+ sync.control_register = 0x22;
+ sync.enable = 1;
+ ec_sync_page(&sync, 1, slave->sii.boot_tx_mailbox_size,
+ EC_DIR_INVALID, // use default direction
+ 0, // no PDO xfer
+ datagram->data + EC_SYNC_PAGE_SIZE);
+ slave->configured_tx_mailbox_offset =
+ slave->sii.boot_tx_mailbox_offset;
+ slave->configured_tx_mailbox_size =
+ slave->sii.boot_tx_mailbox_size;
+
+ } else if (slave->sii.sync_count >= 2) { // mailbox configuration provided
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
+ EC_SYNC_PAGE_SIZE * slave->sii.sync_count);
+ ec_datagram_zero(datagram);
+
+ for (i = 0; i < 2; i++) {
+ ec_sync_page(&slave->sii.syncs[i], i,
+ slave->sii.syncs[i].default_length,
+ NULL, // use default sync manager configuration
+ 0, // no PDO xfer
+ datagram->data + EC_SYNC_PAGE_SIZE * i);
+ }
+
+ slave->configured_rx_mailbox_offset =
+ slave->sii.syncs[0].physical_start_address;
+ slave->configured_rx_mailbox_size =
+ slave->sii.syncs[0].default_length;
+ slave->configured_tx_mailbox_offset =
+ slave->sii.syncs[1].physical_start_address;
+ slave->configured_tx_mailbox_size =
+ slave->sii.syncs[1].default_length;
+ } else { // no mailbox sync manager configurations provided
+ ec_sync_t sync;
+
+ EC_SLAVE_DBG(slave, 1, "Slave does not provide"
+ " mailbox sync manager configurations.\n");
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0800,
+ EC_SYNC_PAGE_SIZE * 2);
+ ec_datagram_zero(datagram);
+
+ ec_sync_init(&sync, slave);
+ sync.physical_start_address = slave->sii.std_rx_mailbox_offset;
+ sync.control_register = 0x26;
+ sync.enable = 1;
+ ec_sync_page(&sync, 0, slave->sii.std_rx_mailbox_size,
+ NULL, // use default sync manager configuration
+ 0, // no PDO xfer
+ datagram->data);
+ slave->configured_rx_mailbox_offset =
+ slave->sii.std_rx_mailbox_offset;
+ slave->configured_rx_mailbox_size =
+ slave->sii.std_rx_mailbox_size;
+
+ ec_sync_init(&sync, slave);
+ sync.physical_start_address = slave->sii.std_tx_mailbox_offset;
+ sync.control_register = 0x22;
+ sync.enable = 1;
+ ec_sync_page(&sync, 1, slave->sii.std_tx_mailbox_size,
+ NULL, // use default sync manager configuration
+ 0, // no PDO xfer
+ datagram->data + EC_SYNC_PAGE_SIZE);
+ slave->configured_tx_mailbox_offset =
+ slave->sii.std_tx_mailbox_offset;
+ slave->configured_tx_mailbox_size =
+ slave->sii.std_tx_mailbox_size;
+ }
+
+ fsm->take_time = 1;
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_mbox_sync;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: SYNC.
+ *
+ * \todo Timeout for response.
+ */
+void ec_fsm_slave_config_state_mbox_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive sync manager"
+ " configuration datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (fsm->take_time) {
+ fsm->take_time = 0;
+ fsm->jiffies_start = datagram->jiffies_sent;
+ }
+
+ /* Because the sync manager configurations are cleared during the last
+ * cycle, some slaves do not immediately respond to the mailbox sync
+ * manager configuration datagram. Therefore, resend the datagram for
+ * a certain time, if the slave does not respond.
+ */
+ if (datagram->working_counter == 0) {
+ unsigned long diff = datagram->jiffies_received - fsm->jiffies_start;
+
+ if (diff >= HZ) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Timeout while configuring"
+ " mailbox sync managers.\n");
+ return;
+ } else {
+ EC_SLAVE_DBG(slave, 1, "Resending after %u ms...\n",
+ (unsigned int) diff * 1000 / HZ);
+ }
+
+ // send configuration datagram again
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+ else if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set sync managers: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+#ifdef EC_SII_ASSIGN
+ ec_fsm_slave_config_enter_assign_pdi(fsm);
+#else
+ ec_fsm_slave_config_enter_boot_preop(fsm);
+#endif
+}
+
+/*****************************************************************************/
+
+#ifdef EC_SII_ASSIGN
+
+/** Assign SII to PDI.
+ */
+void ec_fsm_slave_config_enter_assign_pdi(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (fsm->slave->requested_state != EC_SLAVE_STATE_BOOT) {
+ EC_SLAVE_DBG(slave, 1, "Assigning SII access to PDI.\n");
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0500, 0x01);
+ EC_WRITE_U8(datagram->data, 0x01); // PDI
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_assign_pdi;
+ }
+ else {
+ ec_fsm_slave_config_enter_boot_preop(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: ASSIGN_PDI.
+ */
+void ec_fsm_slave_config_state_assign_pdi(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_WARN(slave, "Failed receive SII assignment datagram: ");
+ ec_datagram_print_state(datagram);
+ goto cont_preop;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(slave, "Failed to assign SII to PDI: ");
+ ec_datagram_print_wc_error(datagram);
+ }
+
+cont_preop:
+ ec_fsm_slave_config_enter_boot_preop(fsm);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/** Request PREOP state.
+ */
+void ec_fsm_slave_config_enter_boot_preop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ fsm->state = ec_fsm_slave_config_state_boot_preop;
+
+ if (fsm->slave->requested_state != EC_SLAVE_STATE_BOOT) {
+ ec_fsm_change_start(fsm->fsm_change,
+ fsm->slave, EC_SLAVE_STATE_PREOP);
+ } else { // BOOT
+ ec_fsm_change_start(fsm->fsm_change,
+ fsm->slave, EC_SLAVE_STATE_BOOT);
+ }
+
+ ec_fsm_change_exec(fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: BOOT/PREOP.
+ */
+void ec_fsm_slave_config_state_boot_preop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(fsm->fsm_change)) {
+ return;
+ }
+
+ if (!ec_fsm_change_success(fsm->fsm_change)) {
+ if (!fsm->fsm_change->spontaneous_change)
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ // slave is now in BOOT or PREOP
+ slave->jiffies_preop = fsm->datagram->jiffies_received;
+
+ EC_SLAVE_DBG(slave, 1, "Now in %s.\n",
+ slave->requested_state != EC_SLAVE_STATE_BOOT ? "PREOP" : "BOOT");
+
+#ifdef EC_SII_ASSIGN
+ EC_SLAVE_DBG(slave, 1, "Assigning SII access back to EtherCAT.\n");
+
+ ec_datagram_fpwr(fsm->datagram, slave->station_address, 0x0500, 0x01);
+ EC_WRITE_U8(fsm->datagram->data, 0x00); // EtherCAT
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_assign_ethercat;
+#else
+ if (slave->current_state == slave->requested_state) {
+ fsm->state = ec_fsm_slave_config_state_end; // successful
+ EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
+ return;
+ }
+
+ ec_fsm_slave_config_enter_sdo_conf(fsm);
+#endif
+}
+
+/*****************************************************************************/
+
+#ifdef EC_SII_ASSIGN
+
+/** Slave configuration state: ASSIGN_ETHERCAT.
+ */
+void ec_fsm_slave_config_state_assign_ethercat(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_WARN(slave, "Failed receive SII assignment datagram: ");
+ ec_datagram_print_state(datagram);
+ goto cont_sdo_conf;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(slave, "Failed to assign SII back to EtherCAT: ");
+ ec_datagram_print_wc_error(datagram);
+ }
+
+cont_sdo_conf:
+ if (slave->current_state == slave->requested_state) {
+ fsm->state = ec_fsm_slave_config_state_end; // successful
+ EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
+ return;
+ }
+
+ ec_fsm_slave_config_enter_sdo_conf(fsm);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/** Check for SDO configurations to be applied.
+ */
+void ec_fsm_slave_config_enter_sdo_conf(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (!slave->config) {
+ ec_fsm_slave_config_enter_pdo_sync(fsm);
+ return;
+ }
+
+ // No CoE configuration to be applied?
+ if (list_empty(&slave->config->sdo_configs)) { // skip SDO configuration
+ ec_fsm_slave_config_enter_soe_conf_preop(fsm);
+ return;
+ }
+
+ // start SDO configuration
+ fsm->state = ec_fsm_slave_config_state_sdo_conf;
+ fsm->request = list_entry(fsm->slave->config->sdo_configs.next,
+ ec_sdo_request_t, list);
+ ec_sdo_request_copy(&fsm->request_copy, fsm->request);
+ ecrt_sdo_request_write(&fsm->request_copy);
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request_copy);
+ ec_fsm_coe_exec(fsm->fsm_coe, fsm->datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: SDO_CONF.
+ */
+void ec_fsm_slave_config_state_sdo_conf(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ if (ec_fsm_coe_exec(fsm->fsm_coe, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_coe_success(fsm->fsm_coe)) {
+ EC_SLAVE_ERR(fsm->slave, "SDO configuration failed.\n");
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ if (!fsm->slave->config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ // Another SDO to configure?
+ if (fsm->request->list.next != &fsm->slave->config->sdo_configs) {
+ fsm->request = list_entry(fsm->request->list.next,
+ ec_sdo_request_t, list);
+ ec_sdo_request_copy(&fsm->request_copy, fsm->request);
+ ecrt_sdo_request_write(&fsm->request_copy);
+ ec_fsm_coe_transfer(fsm->fsm_coe, fsm->slave, &fsm->request_copy);
+ ec_fsm_coe_exec(fsm->fsm_coe, fsm->datagram); // execute immediately
+ return;
+ }
+
+ // All SDOs are now configured.
+ ec_fsm_slave_config_enter_soe_conf_preop(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for SoE configurations to be applied.
+ */
+void ec_fsm_slave_config_enter_soe_conf_preop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *req;
+
+ if (!slave->config) {
+ ec_fsm_slave_config_enter_pdo_sync(fsm);
+ return;
+ }
+
+ list_for_each_entry(req, &slave->config->soe_configs, list) {
+ if (req->al_state == EC_AL_STATE_PREOP) {
+ // start SoE configuration
+ fsm->state = ec_fsm_slave_config_state_soe_conf_preop;
+ fsm->soe_request = req;
+ ec_soe_request_copy(&fsm->soe_request_copy, fsm->soe_request);
+ ec_soe_request_write(&fsm->soe_request_copy);
+ ec_fsm_soe_transfer(fsm->fsm_soe, fsm->slave,
+ &fsm->soe_request_copy);
+ ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram);
+ return;
+ }
+ }
+
+ // No SoE configuration to be applied in PREOP
+ ec_fsm_slave_config_enter_pdo_conf(fsm);
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: SOE_CONF.
+ */
+void ec_fsm_slave_config_state_soe_conf_preop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_soe_success(fsm->fsm_soe)) {
+ EC_SLAVE_ERR(slave, "SoE configuration failed.\n");
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ if (!fsm->slave->config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ // Another IDN to configure in PREOP?
+ while (fsm->soe_request->list.next != &fsm->slave->config->soe_configs) {
+ fsm->soe_request = list_entry(fsm->soe_request->list.next,
+ ec_soe_request_t, list);
+ if (fsm->soe_request->al_state == EC_AL_STATE_PREOP) {
+ ec_soe_request_copy(&fsm->soe_request_copy, fsm->soe_request);
+ ec_soe_request_write(&fsm->soe_request_copy);
+ ec_fsm_soe_transfer(fsm->fsm_soe, fsm->slave,
+ &fsm->soe_request_copy);
+ ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram);
+ return;
+ }
+ }
+
+ // All PREOP IDNs are now configured.
+ ec_fsm_slave_config_enter_pdo_conf(fsm);
+}
+
+/*****************************************************************************/
+
+/** PDO_CONF entry function.
+ */
+void ec_fsm_slave_config_enter_pdo_conf(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ // Start configuring PDOs
+ ec_fsm_pdo_start_configuration(fsm->fsm_pdo, fsm->slave);
+ fsm->state = ec_fsm_slave_config_state_pdo_conf;
+ fsm->state(fsm); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: PDO_CONF.
+ */
+void ec_fsm_slave_config_state_pdo_conf(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ // TODO check for config here
+
+ if (ec_fsm_pdo_exec(fsm->fsm_pdo, fsm->datagram)) {
+ return;
+ }
+
+ if (!fsm->slave->config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ if (!ec_fsm_pdo_success(fsm->fsm_pdo)) {
+ EC_SLAVE_WARN(fsm->slave, "PDO configuration failed.\n");
+ }
+
+ ec_fsm_slave_config_enter_watchdog_divider(fsm);
+}
+
+/*****************************************************************************/
+
+/** WATCHDOG_DIVIDER entry function.
+ */
+void ec_fsm_slave_config_enter_watchdog_divider(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_config_t *config = slave->config;
+
+ if (config && config->watchdog_divider) {
+ EC_SLAVE_DBG(slave, 1, "Setting watchdog divider to %u.\n",
+ config->watchdog_divider);
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0400, 2);
+ EC_WRITE_U16(datagram->data, config->watchdog_divider);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_watchdog_divider;
+ } else {
+ ec_fsm_slave_config_enter_watchdog(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: WATCHDOG_DIVIDER.
+ */
+void ec_fsm_slave_config_state_watchdog_divider(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive watchdog divider"
+ " configuration datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ EC_SLAVE_WARN(slave, "Failed to set watchdog divider: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_watchdog(fsm);
+}
+
+/*****************************************************************************/
+
+/** WATCHDOG entry function
+ */
+void ec_fsm_slave_config_enter_watchdog(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_slave_config_t *config = slave->config;
+
+ if (config && config->watchdog_intervals) {
+ EC_SLAVE_DBG(slave, 1, "Setting process data"
+ " watchdog intervals to %u.\n", config->watchdog_intervals);
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0420, 2);
+ EC_WRITE_U16(datagram->data, config->watchdog_intervals);
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_watchdog;
+ } else {
+ ec_fsm_slave_config_enter_pdo_sync(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: WATCHDOG.
+ */
+
+void ec_fsm_slave_config_state_watchdog(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive sync manager"
+ " watchdog configuration datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(slave, "Failed to set process data"
+ " watchdog intervals: ");
+ ec_datagram_print_wc_error(datagram);
+ }
+
+ ec_fsm_slave_config_enter_pdo_sync(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for PDO sync managers to be configured.
+ */
+void ec_fsm_slave_config_enter_pdo_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+ unsigned int i, j, offset, num_pdo_syncs;
+ uint8_t sync_index;
+ const ec_sync_t *sync;
+ uint16_t size;
+
+ if (slave->sii.mailbox_protocols) {
+ offset = 2; // slave has mailboxes
+ } else {
+ offset = 0;
+ }
+
+ if (slave->sii.sync_count <= offset) {
+ // no PDO sync managers to configure
+ ec_fsm_slave_config_enter_fmmu(fsm);
+ return;
+ }
+
+ num_pdo_syncs = slave->sii.sync_count - offset;
+
+ // configure sync managers for process data
+ ec_datagram_fpwr(datagram, slave->station_address,
+ 0x0800 + EC_SYNC_PAGE_SIZE * offset,
+ EC_SYNC_PAGE_SIZE * num_pdo_syncs);
+ ec_datagram_zero(datagram);
+
+ for (i = 0; i < num_pdo_syncs; i++) {
+ const ec_sync_config_t *sync_config;
+ uint8_t pdo_xfer = 0;
+ sync_index = i + offset;
+ sync = &slave->sii.syncs[sync_index];
+
+ if (slave->config) {
+ const ec_slave_config_t *sc = slave->config;
+ sync_config = &sc->sync_configs[sync_index];
+ size = ec_pdo_list_total_size(&sync_config->pdos);
+
+ // determine, if PDOs shall be transferred via this SM
+ // inthat case, enable sync manager in every case
+ for (j = 0; j < sc->used_fmmus; j++) {
+ if (sc->fmmu_configs[j].sync_index == sync_index) {
+ pdo_xfer = 1;
+ break;
+ }
+ }
+
+ } else {
+ sync_config = NULL;
+ size = sync->default_length;
+ }
+
+ ec_sync_page(sync, sync_index, size, sync_config, pdo_xfer,
+ datagram->data + EC_SYNC_PAGE_SIZE * i);
+ }
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_pdo_sync;
+}
+
+/*****************************************************************************/
+
+/** Configure PDO sync managers.
+ */
+void ec_fsm_slave_config_state_pdo_sync(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive process data sync"
+ " manager configuration datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set process data sync managers: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_fmmu(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for FMMUs to be configured.
+ */
+void ec_fsm_slave_config_enter_fmmu(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_datagram_t *datagram = fsm->datagram;
+ unsigned int i;
+ const ec_fmmu_config_t *fmmu;
+ const ec_sync_t *sync;
+
+ if (!slave->config) {
+ ec_fsm_slave_config_enter_safeop(fsm);
+ return;
+ }
+
+ if (slave->base_fmmu_count < slave->config->used_fmmus) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Slave has less FMMUs (%u)"
+ " than requested (%u).\n", slave->base_fmmu_count,
+ slave->config->used_fmmus);
+ return;
+ }
+
+ if (!slave->base_fmmu_count) { // skip FMMU configuration
+ ec_fsm_slave_config_enter_dc_cycle(fsm);
+ return;
+ }
+
+ // configure FMMUs
+ ec_datagram_fpwr(datagram, slave->station_address,
+ 0x0600, EC_FMMU_PAGE_SIZE * slave->base_fmmu_count);
+ ec_datagram_zero(datagram);
+ for (i = 0; i < slave->config->used_fmmus; i++) {
+ fmmu = &slave->config->fmmu_configs[i];
+ if (!(sync = ec_slave_get_sync(slave, fmmu->sync_index))) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to determine PDO sync manager"
+ " for FMMU!\n");
+ return;
+ }
+ ec_fmmu_config_page(fmmu, sync,
+ datagram->data + EC_FMMU_PAGE_SIZE * i);
+ }
+
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_fmmu;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: FMMU.
+ */
+void ec_fsm_slave_config_state_fmmu(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive FMMUs datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set FMMUs: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_dc_cycle(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for DC to be configured.
+ */
+void ec_fsm_slave_config_enter_dc_cycle(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_slave_config_t *config = slave->config;
+
+ if (!config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ if (config->dc_assign_activate) {
+ if (!slave->base_dc_supported || !slave->has_dc_system_time) {
+ EC_SLAVE_WARN(slave, "Slave seems not to support"
+ " distributed clocks!\n");
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Setting DC cycle times to %u / %u.\n",
+ config->dc_sync[0].cycle_time, config->dc_sync[1].cycle_time);
+
+ // set DC cycle times
+ ec_datagram_fpwr(datagram, slave->station_address, 0x09A0, 8);
+ EC_WRITE_U32(datagram->data, config->dc_sync[0].cycle_time);
+ EC_WRITE_U32(datagram->data + 4, config->dc_sync[1].cycle_time);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_dc_cycle;
+ } else {
+ // DC are unused
+ ec_fsm_slave_config_enter_safeop(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: DC CYCLE.
+ */
+void ec_fsm_slave_config_state_dc_cycle(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_slave_config_t *config = slave->config;
+
+ if (!config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive DC cycle times datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set DC cycle times: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Checking for synchrony.\n");
+
+ fsm->jiffies_start = jiffies;
+ ec_datagram_fprd(datagram, slave->station_address, 0x092c, 4);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_dc_sync_check;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: DC SYNC CHECK.
+ */
+void ec_fsm_slave_config_state_dc_sync_check(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ ec_slave_config_t *config = slave->config;
+ uint32_t abs_sync_diff;
+ unsigned long diff_ms;
+ ec_sync_signal_t *sync0 = &config->dc_sync[0];
+ ec_sync_signal_t *sync1 = &config->dc_sync[1];
+ u64 start_time;
+
+ if (!config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive DC sync check datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to check DC synchrony: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ abs_sync_diff = EC_READ_U32(datagram->data) & 0x7fffffff;
+ diff_ms = (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
+
+ if (abs_sync_diff > EC_DC_MAX_SYNC_DIFF_NS) {
+
+ if (diff_ms >= EC_DC_SYNC_WAIT_MS) {
+ EC_SLAVE_WARN(slave, "Slave did not sync after %lu ms.\n",
+ diff_ms);
+ } else {
+ EC_SLAVE_DBG(slave, 1, "Sync after %4lu ms: %10u ns\n",
+ diff_ms, abs_sync_diff);
+
+ // check synchrony again
+ ec_datagram_fprd(datagram, slave->station_address, 0x092c, 4);
+ fsm->retries = EC_FSM_RETRIES;
+ return;
+ }
+ } else {
+ EC_SLAVE_DBG(slave, 1, "%u ns difference after %lu ms.\n",
+ abs_sync_diff, diff_ms);
+ }
+
+ // set DC start time (roughly in the future, not in-phase)
+ start_time = master->app_time + EC_DC_START_OFFSET; // now + X ns
+
+ if (sync0->cycle_time) {
+ // find correct phase
+ if (master->dc_ref_time) {
+ u64 diff, start;
+ u32 remainder, cycle;
+
+ diff = start_time - master->dc_ref_time;
+ cycle = sync0->cycle_time + sync1->cycle_time;
+ remainder = do_div(diff, cycle);
+
+ start = start_time + cycle - remainder + sync0->shift_time;
+
+ EC_SLAVE_DBG(slave, 1, " ref_time=%llu\n", master->dc_ref_time);
+ EC_SLAVE_DBG(slave, 1, " app_time=%llu\n", master->app_time);
+ EC_SLAVE_DBG(slave, 1, " start_time=%llu\n", start_time);
+ EC_SLAVE_DBG(slave, 1, " cycle=%u\n", cycle);
+ EC_SLAVE_DBG(slave, 1, " shift_time=%i\n", sync0->shift_time);
+ EC_SLAVE_DBG(slave, 1, " remainder=%u\n", remainder);
+ EC_SLAVE_DBG(slave, 1, " start=%llu\n", start);
+ start_time = start;
+ } else {
+ EC_SLAVE_WARN(slave, "No application time supplied."
+ " Cyclic start time will not be in phase.\n");
+ }
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Setting DC cyclic operation"
+ " start time to %llu.\n", start_time);
+
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0990, 8);
+ EC_WRITE_U64(datagram->data, start_time);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_dc_start;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: DC START.
+ */
+void ec_fsm_slave_config_state_dc_start(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ ec_slave_config_t *config = slave->config;
+
+ if (!config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive DC start time datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to set DC start time: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Setting DC AssignActivate to 0x%04x.\n",
+ config->dc_assign_activate);
+
+ // assign sync unit to EtherCAT or PDI
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0980, 2);
+ EC_WRITE_U16(datagram->data, config->dc_assign_activate);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_config_state_dc_assign;
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: DC ASSIGN.
+ */
+void ec_fsm_slave_config_state_dc_assign(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive DC activation datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ EC_SLAVE_ERR(slave, "Failed to activate DC: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ ec_fsm_slave_config_enter_safeop(fsm);
+}
+
+/*****************************************************************************/
+
+/** Request SAFEOP state.
+ */
+void ec_fsm_slave_config_enter_safeop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ fsm->state = ec_fsm_slave_config_state_safeop;
+ ec_fsm_change_start(fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_SAFEOP);
+ ec_fsm_change_exec(fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: SAFEOP.
+ */
+void ec_fsm_slave_config_state_safeop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(fsm->fsm_change)) {
+ if (!fsm->fsm_change->spontaneous_change)
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ // slave is now in SAFEOP
+
+ EC_SLAVE_DBG(slave, 1, "Now in SAFEOP.\n");
+
+ if (fsm->slave->current_state == fsm->slave->requested_state) {
+ fsm->state = ec_fsm_slave_config_state_end; // successful
+ EC_SLAVE_DBG(slave, 1, "Finished configuration.\n");
+ return;
+ }
+
+ ec_fsm_slave_config_enter_soe_conf_safeop(fsm);
+}
+
+/*****************************************************************************/
+
+/** Check for SoE configurations to be applied in SAFEOP.
+ */
+void ec_fsm_slave_config_enter_soe_conf_safeop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *req;
+
+ if (!slave->config) {
+ ec_fsm_slave_config_enter_op(fsm);
+ return;
+ }
+
+ list_for_each_entry(req, &slave->config->soe_configs, list) {
+ if (req->al_state == EC_AL_STATE_SAFEOP) {
+ // start SoE configuration
+ fsm->state = ec_fsm_slave_config_state_soe_conf_safeop;
+ fsm->soe_request = req;
+ ec_soe_request_copy(&fsm->soe_request_copy, fsm->soe_request);
+ ec_soe_request_write(&fsm->soe_request_copy);
+ ec_fsm_soe_transfer(fsm->fsm_soe, fsm->slave,
+ &fsm->soe_request_copy);
+ ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram);
+ return;
+ }
+ }
+
+ // No SoE configuration to be applied in SAFEOP
+ ec_fsm_slave_config_enter_op(fsm);
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: SOE_CONF.
+ */
+void ec_fsm_slave_config_state_soe_conf_safeop(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_soe_success(fsm->fsm_soe)) {
+ EC_SLAVE_ERR(slave, "SoE configuration failed.\n");
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ if (!fsm->slave->config) { // config removed in the meantime
+ ec_fsm_slave_config_reconfigure(fsm);
+ return;
+ }
+
+ // Another IDN to configure in SAFEOP?
+ while (fsm->soe_request->list.next != &fsm->slave->config->soe_configs) {
+ fsm->soe_request = list_entry(fsm->soe_request->list.next,
+ ec_soe_request_t, list);
+ if (fsm->soe_request->al_state == EC_AL_STATE_SAFEOP) {
+ ec_soe_request_copy(&fsm->soe_request_copy, fsm->soe_request);
+ ec_soe_request_write(&fsm->soe_request_copy);
+ ec_fsm_soe_transfer(fsm->fsm_soe, fsm->slave,
+ &fsm->soe_request_copy);
+ ec_fsm_soe_exec(fsm->fsm_soe, fsm->datagram);
+ return;
+ }
+ }
+
+ // All SAFEOP IDNs are now configured.
+ ec_fsm_slave_config_enter_op(fsm);
+}
+
+/*****************************************************************************/
+
+/** Bring slave to OP.
+ */
+void ec_fsm_slave_config_enter_op(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ // set state to OP
+ fsm->state = ec_fsm_slave_config_state_op;
+ ec_fsm_change_start(fsm->fsm_change, fsm->slave, EC_SLAVE_STATE_OP);
+ ec_fsm_change_exec(fsm->fsm_change); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave configuration state: OP
+ */
+void ec_fsm_slave_config_state_op(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ if (ec_fsm_change_exec(fsm->fsm_change)) return;
+
+ if (!ec_fsm_change_success(fsm->fsm_change)) {
+ if (!fsm->fsm_change->spontaneous_change)
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_config_state_error;
+ return;
+ }
+
+ // slave is now in OP
+
+ EC_SLAVE_DBG(slave, 1, "Now in OP. Finished configuration.\n");
+
+ fsm->state = ec_fsm_slave_config_state_end; // successful
+}
+
+/*****************************************************************************/
+
+/** Reconfigure the slave starting at INIT.
+ */
+void ec_fsm_slave_config_reconfigure(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+ EC_SLAVE_DBG(fsm->slave, 1, "Slave configuration detached during "
+ "configuration. Reconfiguring.");
+
+ ec_fsm_slave_config_enter_init(fsm); // reconfigure
+}
+
+/******************************************************************************
+ * Common state functions
+ *****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_slave_config_state_error(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_slave_config_state_end(
+ ec_fsm_slave_config_t *fsm /**< slave state machine */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_slave_config.h b/net/ethercat/master/fsm_slave_config.h
new file mode 100644
index 000000000000..0111be7bdaa1
--- /dev/null
+++ b/net/ethercat/master/fsm_slave_config.h
@@ -0,0 +1,86 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave configuration state machine.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SLAVE_CONFIG_H__
+#define __EC_FSM_SLAVE_CONFIG_H__
+
+#include "globals.h"
+#include "slave.h"
+#include "datagram.h"
+#include "fsm_change.h"
+#include "fsm_coe.h"
+#include "fsm_pdo.h"
+
+/*****************************************************************************/
+
+/** \see ec_fsm_slave_config */
+typedef struct ec_fsm_slave_config ec_fsm_slave_config_t;
+
+/** Finite state machine to configure an EtherCAT slave.
+ */
+struct ec_fsm_slave_config
+{
+ ec_datagram_t *datagram; /**< Datagram used in the state machine. */
+ ec_fsm_change_t *fsm_change; /**< State change state machine. */
+ ec_fsm_coe_t *fsm_coe; /**< CoE state machine. */
+ ec_fsm_soe_t *fsm_soe; /**< SoE state machine. */
+ ec_fsm_pdo_t *fsm_pdo; /**< PDO configuration state machine. */
+
+ ec_slave_t *slave; /**< Slave the FSM runs on. */
+ void (*state)(ec_fsm_slave_config_t *); /**< State function. */
+ unsigned int retries; /**< Retries on datagram timeout. */
+ ec_sdo_request_t *request; /**< SDO request for SDO configuration. */
+ ec_sdo_request_t request_copy; /**< Copied SDO request. */
+ ec_soe_request_t *soe_request; /**< SDO request for SDO configuration. */
+ ec_soe_request_t soe_request_copy; /**< Copied SDO request. */
+ unsigned long jiffies_start; /**< For timeout calculations. */
+ unsigned int take_time; /**< Store jiffies after datagram reception. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_slave_config_init(ec_fsm_slave_config_t *, ec_datagram_t *,
+ ec_fsm_change_t *, ec_fsm_coe_t *, ec_fsm_soe_t *, ec_fsm_pdo_t *);
+void ec_fsm_slave_config_clear(ec_fsm_slave_config_t *);
+
+void ec_fsm_slave_config_start(ec_fsm_slave_config_t *, ec_slave_t *);
+
+int ec_fsm_slave_config_exec(ec_fsm_slave_config_t *);
+int ec_fsm_slave_config_success(const ec_fsm_slave_config_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_slave_scan.c b/net/ethercat/master/fsm_slave_scan.c
new file mode 100644
index 000000000000..6f36ba23506e
--- /dev/null
+++ b/net/ethercat/master/fsm_slave_scan.c
@@ -0,0 +1,1121 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave state machines.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "slave_config.h"
+
+#include "fsm_slave_scan.h"
+
+/*****************************************************************************/
+
+void ec_fsm_slave_scan_state_start(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_address(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_state(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_base(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_dc_cap(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_dc_times(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_datalink(ec_fsm_slave_scan_t *);
+#ifdef EC_SII_ASSIGN
+void ec_fsm_slave_scan_state_assign_sii(ec_fsm_slave_scan_t *);
+#endif
+void ec_fsm_slave_scan_state_sii_size(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_sii_data(ec_fsm_slave_scan_t *);
+#ifdef EC_REGALIAS
+void ec_fsm_slave_scan_state_regalias(ec_fsm_slave_scan_t *);
+#endif
+void ec_fsm_slave_scan_state_preop(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_sync(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_pdos(ec_fsm_slave_scan_t *);
+
+void ec_fsm_slave_scan_state_end(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_state_error(ec_fsm_slave_scan_t *);
+
+void ec_fsm_slave_scan_enter_datalink(ec_fsm_slave_scan_t *);
+#ifdef EC_REGALIAS
+void ec_fsm_slave_scan_enter_regalias(ec_fsm_slave_scan_t *);
+#endif
+void ec_fsm_slave_scan_enter_preop(ec_fsm_slave_scan_t *);
+void ec_fsm_slave_scan_enter_pdos(ec_fsm_slave_scan_t *);
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_slave_scan_init(
+ ec_fsm_slave_scan_t *fsm, /**< Slave scanning state machine. */
+ ec_datagram_t *datagram, /**< Datagram to use. */
+ ec_fsm_slave_config_t *fsm_slave_config, /**< Slave configuration
+ state machine to use. */
+ ec_fsm_pdo_t *fsm_pdo /**< PDO configuration machine to use. */
+ )
+{
+ fsm->datagram = datagram;
+ fsm->fsm_slave_config = fsm_slave_config;
+ fsm->fsm_pdo = fsm_pdo;
+
+ // init sub state machines
+ ec_fsm_sii_init(&fsm->fsm_sii, fsm->datagram);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_slave_scan_clear(ec_fsm_slave_scan_t *fsm /**< slave state machine */)
+{
+ // clear sub state machines
+ ec_fsm_sii_clear(&fsm->fsm_sii);
+}
+
+/*****************************************************************************/
+
+/**
+ * Start slave scan state machine.
+ */
+
+void ec_fsm_slave_scan_start(
+ ec_fsm_slave_scan_t *fsm, /**< slave state machine */
+ ec_slave_t *slave /**< slave to configure */
+ )
+{
+ fsm->slave = slave;
+ fsm->state = ec_fsm_slave_scan_state_start;
+}
+
+/*****************************************************************************/
+
+/**
+ \return false, if state machine has terminated
+*/
+
+int ec_fsm_slave_scan_running(const ec_fsm_slave_scan_t *fsm /**< slave state machine */)
+{
+ return fsm->state != ec_fsm_slave_scan_state_end
+ && fsm->state != ec_fsm_slave_scan_state_error;
+}
+
+/*****************************************************************************/
+
+/**
+ Executes the current state of the state machine.
+ If the state machine's datagram is not sent or received yet, the execution
+ of the state machine is delayed to the next cycle.
+ \return false, if state machine has terminated
+*/
+
+int ec_fsm_slave_scan_exec(ec_fsm_slave_scan_t *fsm /**< slave state machine */)
+{
+ if (fsm->datagram->state == EC_DATAGRAM_SENT
+ || fsm->datagram->state == EC_DATAGRAM_QUEUED) {
+ // datagram was not sent or received yet.
+ return ec_fsm_slave_scan_running(fsm);
+ }
+
+ fsm->state(fsm);
+ return ec_fsm_slave_scan_running(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ \return true, if the state machine terminated gracefully
+*/
+
+int ec_fsm_slave_scan_success(const ec_fsm_slave_scan_t *fsm /**< slave state machine */)
+{
+ return fsm->state == ec_fsm_slave_scan_state_end;
+}
+
+/******************************************************************************
+ * slave scan state machine
+ *****************************************************************************/
+
+/**
+ Slave scan state: START.
+ First state of the slave state machine. Writes the station address to the
+ slave, according to its ring position.
+*/
+
+void ec_fsm_slave_scan_state_start(ec_fsm_slave_scan_t *fsm /**< slave state machine */)
+{
+ // write station address
+ ec_datagram_apwr(fsm->datagram, fsm->slave->ring_position, 0x0010, 2);
+ EC_WRITE_U16(fsm->datagram->data, fsm->slave->station_address);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_address;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: ADDRESS.
+*/
+
+void ec_fsm_slave_scan_state_address(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(fsm->slave,
+ "Failed to receive station address datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(fsm->slave, "Failed to write station address: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ // Read AL state
+ ec_datagram_fprd(datagram, fsm->slave->station_address, 0x0130, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_state;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: STATE.
+*/
+
+void ec_fsm_slave_scan_state_state(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive AL state datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to read AL state: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ slave->current_state = EC_READ_U8(datagram->data);
+ if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+ char state_str[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, state_str, 0);
+ EC_SLAVE_WARN(slave, "Slave has state error bit set (%s)!\n",
+ state_str);
+ }
+
+ // read base data
+ ec_datagram_fprd(datagram, fsm->slave->station_address, 0x0000, 12);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_base;
+}
+
+/*****************************************************************************/
+
+/** Slave scan state: BASE.
+ */
+void ec_fsm_slave_scan_state_base(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ u8 octet;
+ int i;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive base data datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to read base data: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ slave->base_type = EC_READ_U8 (datagram->data);
+ slave->base_revision = EC_READ_U8 (datagram->data + 1);
+ slave->base_build = EC_READ_U16(datagram->data + 2);
+
+ slave->base_fmmu_count = EC_READ_U8 (datagram->data + 4);
+ if (slave->base_fmmu_count > EC_MAX_FMMUS) {
+ EC_SLAVE_WARN(slave, "Slave has more FMMUs (%u) than the master can"
+ " handle (%u).\n", slave->base_fmmu_count, EC_MAX_FMMUS);
+ slave->base_fmmu_count = EC_MAX_FMMUS;
+ }
+
+ slave->base_sync_count = EC_READ_U8(datagram->data + 5);
+ if (slave->base_sync_count > EC_MAX_SYNC_MANAGERS) {
+ EC_SLAVE_WARN(slave, "Slave provides more sync managers (%u)"
+ " than the master can handle (%u).\n",
+ slave->base_sync_count, EC_MAX_SYNC_MANAGERS);
+ slave->base_sync_count = EC_MAX_SYNC_MANAGERS;
+ }
+
+ octet = EC_READ_U8(datagram->data + 7);
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ slave->ports[i].desc = (octet >> (2 * i)) & 0x03;
+ }
+
+ octet = EC_READ_U8(datagram->data + 8);
+ slave->base_fmmu_bit_operation = octet & 0x01;
+ slave->base_dc_supported = (octet >> 2) & 0x01;
+ slave->base_dc_range = ((octet >> 3) & 0x01) ? EC_DC_64 : EC_DC_32;
+
+ if (slave->base_dc_supported) {
+ // read DC capabilities
+ ec_datagram_fprd(datagram, slave->station_address, 0x0910,
+ slave->base_dc_range == EC_DC_64 ? 8 : 4);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_dc_cap;
+ } else {
+ ec_fsm_slave_scan_enter_datalink(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: DC CAPABILITIES.
+*/
+
+void ec_fsm_slave_scan_state_dc_cap(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive system time datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter == 1) {
+ slave->has_dc_system_time = 1;
+ EC_SLAVE_DBG(slave, 1, "Slave has the System Time register.\n");
+ } else if (datagram->working_counter == 0) {
+ EC_SLAVE_DBG(slave, 1, "Slave has no System Time register; delay "
+ "measurement only.\n");
+ } else {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to determine, if system time register is "
+ "supported: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ // read DC port receive times
+ ec_datagram_fprd(datagram, slave->station_address, 0x0900, 16);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_dc_times;
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: DC TIMES.
+*/
+
+void ec_fsm_slave_scan_state_dc_times(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ int i;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive system time datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to get DC receive times: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ slave->ports[i].receive_time = EC_READ_U32(datagram->data + 4 * i);
+ }
+
+ ec_fsm_slave_scan_enter_datalink(fsm);
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan entry function: DATALINK.
+*/
+
+void ec_fsm_slave_scan_enter_datalink(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ // read data link status
+ ec_datagram_fprd(datagram, slave->station_address, 0x0110, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_datalink;
+}
+
+/*****************************************************************************/
+
+/** Enter slave scan state SII_SIZE.
+ */
+void ec_fsm_slave_scan_enter_sii_size(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ // Start fetching SII size
+
+ EC_SLAVE_DBG(fsm->slave, 1, "Determining SII size.\n");
+
+ fsm->sii_offset = EC_FIRST_SII_CATEGORY_OFFSET; // first category header
+ ec_fsm_sii_read(&fsm->fsm_sii, fsm->slave, fsm->sii_offset,
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ fsm->state = ec_fsm_slave_scan_state_sii_size;
+ fsm->state(fsm); // execute state immediately
+}
+
+/*****************************************************************************/
+
+#ifdef EC_SII_ASSIGN
+
+/** Enter slave scan state ASSIGN_SII.
+ */
+void ec_fsm_slave_scan_enter_assign_sii(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ EC_SLAVE_DBG(slave, 1, "Assigning SII access to EtherCAT.\n");
+
+ // assign SII to ECAT
+ ec_datagram_fpwr(datagram, slave->station_address, 0x0500, 1);
+ EC_WRITE_U8(datagram->data, 0x00); // EtherCAT
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_assign_sii;
+}
+
+#endif
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: DATALINK.
+*/
+
+void ec_fsm_slave_scan_state_datalink(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ uint16_t dl_status;
+ unsigned int i;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive DL status datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to read DL status: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ dl_status = EC_READ_U16(datagram->data);
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ slave->ports[i].link.link_up =
+ dl_status & (1 << (4 + i)) ? 1 : 0;
+ slave->ports[i].link.loop_closed =
+ dl_status & (1 << (8 + i * 2)) ? 1 : 0;
+ slave->ports[i].link.signal_detected =
+ dl_status & (1 << (9 + i * 2)) ? 1 : 0;
+ }
+
+#ifdef EC_SII_ASSIGN
+ ec_fsm_slave_scan_enter_assign_sii(fsm);
+#else
+ ec_fsm_slave_scan_enter_sii_size(fsm);
+#endif
+}
+
+/*****************************************************************************/
+
+#ifdef EC_SII_ASSIGN
+
+/**
+ Slave scan state: ASSIGN_SII.
+*/
+
+void ec_fsm_slave_scan_state_assign_sii(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ return;
+ }
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ EC_SLAVE_WARN(slave, "Failed to receive SII assignment datagram: ");
+ ec_datagram_print_state(datagram);
+ // Try to go on, probably assignment is correct
+ goto continue_with_sii_size;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_WARN(slave, "Failed to assign SII to EtherCAT: ");
+ ec_datagram_print_wc_error(datagram);
+ // Try to go on, probably assignment is correct
+ }
+
+continue_with_sii_size:
+ ec_fsm_slave_scan_enter_sii_size(fsm);
+}
+
+#endif
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: SII SIZE.
+*/
+
+void ec_fsm_slave_scan_state_sii_size(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint16_t cat_type, cat_size;
+
+ if (ec_fsm_sii_exec(&fsm->fsm_sii))
+ return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to determine SII content size:"
+ " Reading word offset 0x%04x failed. Assuming %u words.\n",
+ fsm->sii_offset, EC_FIRST_SII_CATEGORY_OFFSET);
+ slave->sii_nwords = EC_FIRST_SII_CATEGORY_OFFSET;
+ goto alloc_sii;
+ }
+
+ cat_type = EC_READ_U16(fsm->fsm_sii.value);
+ cat_size = EC_READ_U16(fsm->fsm_sii.value + 2);
+
+ if (cat_type != 0xFFFF) { // not the last category
+ off_t next_offset = 2UL + fsm->sii_offset + cat_size;
+
+ EC_SLAVE_DBG(slave, 1, "Found category type %u with size %u."
+ " Proceeding to offset %zu.\n",
+ cat_type, cat_size, next_offset);
+
+ if (next_offset >= EC_MAX_SII_SIZE) {
+ EC_SLAVE_WARN(slave, "SII size exceeds %u words"
+ " (0xffff limiter missing?).\n", EC_MAX_SII_SIZE);
+ // cut off category data...
+ slave->sii_nwords = EC_FIRST_SII_CATEGORY_OFFSET;
+ goto alloc_sii;
+ }
+ fsm->sii_offset = next_offset;
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset,
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
+ return;
+ }
+
+ slave->sii_nwords = fsm->sii_offset + 1;
+
+alloc_sii:
+ if (slave->sii_words) {
+ EC_SLAVE_WARN(slave, "Freeing old SII data...\n");
+ kfree(slave->sii_words);
+ }
+
+ if (!(slave->sii_words =
+ (uint16_t *) kmalloc(slave->sii_nwords * 2, GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate %zu words of SII data.\n",
+ slave->sii_nwords);
+ slave->sii_nwords = 0;
+ slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ return;
+ }
+
+ // Start fetching SII contents
+
+ fsm->state = ec_fsm_slave_scan_state_sii_data;
+ fsm->sii_offset = 0x0000;
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset,
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
+}
+
+/*****************************************************************************/
+
+/**
+ Slave scan state: SII DATA.
+*/
+
+void ec_fsm_slave_scan_state_sii_data(ec_fsm_slave_scan_t *fsm
+ /**< slave state machine */)
+{
+ ec_slave_t *slave = fsm->slave;
+ uint16_t *cat_word, cat_type, cat_size;
+
+ if (ec_fsm_sii_exec(&fsm->fsm_sii)) return;
+
+ if (!ec_fsm_sii_success(&fsm->fsm_sii)) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to fetch SII contents.\n");
+ return;
+ }
+
+ // 2 words fetched
+
+ if (fsm->sii_offset + 2 <= slave->sii_nwords) { // 2 words fit
+ memcpy(slave->sii_words + fsm->sii_offset, fsm->fsm_sii.value, 4);
+ } else { // copy the last word
+ memcpy(slave->sii_words + fsm->sii_offset, fsm->fsm_sii.value, 2);
+ }
+
+ if (fsm->sii_offset + 2 < slave->sii_nwords) {
+ // fetch the next 2 words
+ fsm->sii_offset += 2;
+ ec_fsm_sii_read(&fsm->fsm_sii, slave, fsm->sii_offset,
+ EC_FSM_SII_USE_CONFIGURED_ADDRESS);
+ ec_fsm_sii_exec(&fsm->fsm_sii); // execute state immediately
+ return;
+ }
+
+ // Evaluate SII contents
+
+ ec_slave_clear_sync_managers(slave);
+
+ slave->sii.alias =
+ EC_READ_U16(slave->sii_words + 0x0004);
+ slave->effective_alias = slave->sii.alias;
+ slave->sii.vendor_id =
+ EC_READ_U32(slave->sii_words + 0x0008);
+ slave->sii.product_code =
+ EC_READ_U32(slave->sii_words + 0x000A);
+ slave->sii.revision_number =
+ EC_READ_U32(slave->sii_words + 0x000C);
+ slave->sii.serial_number =
+ EC_READ_U32(slave->sii_words + 0x000E);
+ slave->sii.boot_rx_mailbox_offset =
+ EC_READ_U16(slave->sii_words + 0x0014);
+ slave->sii.boot_rx_mailbox_size =
+ EC_READ_U16(slave->sii_words + 0x0015);
+ slave->sii.boot_tx_mailbox_offset =
+ EC_READ_U16(slave->sii_words + 0x0016);
+ slave->sii.boot_tx_mailbox_size =
+ EC_READ_U16(slave->sii_words + 0x0017);
+ slave->sii.std_rx_mailbox_offset =
+ EC_READ_U16(slave->sii_words + 0x0018);
+ slave->sii.std_rx_mailbox_size =
+ EC_READ_U16(slave->sii_words + 0x0019);
+ slave->sii.std_tx_mailbox_offset =
+ EC_READ_U16(slave->sii_words + 0x001A);
+ slave->sii.std_tx_mailbox_size =
+ EC_READ_U16(slave->sii_words + 0x001B);
+ slave->sii.mailbox_protocols =
+ EC_READ_U16(slave->sii_words + 0x001C);
+ if (slave->sii.mailbox_protocols) {
+ int need_delim = 0;
+ uint16_t all = EC_MBOX_AOE | EC_MBOX_COE | EC_MBOX_FOE |
+ EC_MBOX_SOE | EC_MBOX_VOE;
+ if ((slave->sii.mailbox_protocols & all) &&
+ slave->master->debug_level >= 1) {
+ EC_SLAVE_DBG(slave, 1, "Slave announces to support ");
+ if (slave->sii.mailbox_protocols & EC_MBOX_AOE) {
+ printk(KERN_CONT "AoE");
+ need_delim = 1;
+ }
+ if (slave->sii.mailbox_protocols & EC_MBOX_COE) {
+ if (need_delim) {
+ printk(KERN_CONT ", ");
+ }
+ printk(KERN_CONT "CoE");
+ need_delim = 1;
+ }
+ if (slave->sii.mailbox_protocols & EC_MBOX_FOE) {
+ if (need_delim) {
+ printk(KERN_CONT ", ");
+ }
+ printk(KERN_CONT "FoE");
+ need_delim = 1;
+ }
+ if (slave->sii.mailbox_protocols & EC_MBOX_SOE) {
+ if (need_delim) {
+ printk(KERN_CONT ", ");
+ }
+ printk(KERN_CONT "SoE");
+ need_delim = 1;
+ }
+ if (slave->sii.mailbox_protocols & EC_MBOX_VOE) {
+ if (need_delim) {
+ printk(KERN_CONT ", ");
+ }
+ printk(KERN_CONT "VoE");
+ need_delim = 1;
+ }
+ printk(KERN_CONT ".\n");
+ }
+ if (slave->sii.mailbox_protocols & ~all) {
+ EC_SLAVE_DBG(slave, 1, "Slave announces to support unknown"
+ " mailbox protocols 0x%04X.",
+ slave->sii.mailbox_protocols & ~all);
+ }
+ }
+ else {
+ EC_SLAVE_DBG(slave, 1, "Slave announces to support no mailbox"
+ " protocols.");
+ }
+
+ if (slave->sii.boot_rx_mailbox_offset == 0xffff ||
+ slave->sii.boot_rx_mailbox_size == 0xffff ||
+ slave->sii.boot_tx_mailbox_offset == 0xffff ||
+ slave->sii.boot_tx_mailbox_size == 0xffff ||
+ slave->sii.std_rx_mailbox_offset == 0xffff ||
+ slave->sii.std_rx_mailbox_size == 0xffff ||
+ slave->sii.std_tx_mailbox_offset == 0xffff ||
+ slave->sii.std_tx_mailbox_size == 0xffff) {
+ slave->sii.mailbox_protocols = 0x0000;
+ EC_SLAVE_ERR(slave, "Invalid mailbox settings in SII."
+ " Disabling mailbox communication.");
+ }
+
+ if (slave->sii_nwords == EC_FIRST_SII_CATEGORY_OFFSET) {
+ // sii does not contain category data
+ fsm->state = ec_fsm_slave_scan_state_end;
+ return;
+ }
+
+ if (slave->sii_nwords < EC_FIRST_SII_CATEGORY_OFFSET + 1) {
+ EC_SLAVE_ERR(slave, "Unexpected end of SII data:"
+ " First category header missing.\n");
+ goto end;
+ }
+
+ // evaluate category data
+ cat_word = slave->sii_words + EC_FIRST_SII_CATEGORY_OFFSET;
+ while (EC_READ_U16(cat_word) != 0xFFFF) {
+
+ // type and size words must fit
+ if (cat_word + 2 - slave->sii_words > slave->sii_nwords) {
+ EC_SLAVE_ERR(slave, "Unexpected end of SII data:"
+ " Category header incomplete.\n");
+ goto end;
+ }
+
+ cat_type = EC_READ_U16(cat_word) & 0x7FFF;
+ cat_size = EC_READ_U16(cat_word + 1);
+ cat_word += 2;
+
+ if (cat_word + cat_size - slave->sii_words > slave->sii_nwords) {
+ EC_SLAVE_WARN(slave, "Unexpected end of SII data:"
+ " Category data incomplete.\n");
+ goto end;
+ }
+
+ switch (cat_type) {
+ case 0x000A:
+ if (ec_slave_fetch_sii_strings(slave, (uint8_t *) cat_word,
+ cat_size * 2))
+ goto end;
+ break;
+ case 0x001E:
+ if (ec_slave_fetch_sii_general(slave, (uint8_t *) cat_word,
+ cat_size * 2))
+ goto end;
+ break;
+ case 0x0028:
+ break;
+ case 0x0029:
+ if (ec_slave_fetch_sii_syncs(slave, (uint8_t *) cat_word,
+ cat_size * 2))
+ goto end;
+ break;
+ case 0x0032:
+ if (ec_slave_fetch_sii_pdos( slave, (uint8_t *) cat_word,
+ cat_size * 2, EC_DIR_INPUT)) // TxPDO
+ goto end;
+ break;
+ case 0x0033:
+ if (ec_slave_fetch_sii_pdos( slave, (uint8_t *) cat_word,
+ cat_size * 2, EC_DIR_OUTPUT)) // RxPDO
+ goto end;
+ break;
+ default:
+ EC_SLAVE_DBG(slave, 1, "Unknown category type 0x%04X.\n",
+ cat_type);
+ }
+
+ cat_word += cat_size;
+ if (cat_word - slave->sii_words >= slave->sii_nwords) {
+ EC_SLAVE_WARN(slave, "Unexpected end of SII data:"
+ " Next category header missing.\n");
+ goto end;
+ }
+ }
+
+#ifdef EC_REGALIAS
+ ec_fsm_slave_scan_enter_regalias(fsm);
+#else
+ if (slave->sii.mailbox_protocols & EC_MBOX_COE) {
+ ec_fsm_slave_scan_enter_preop(fsm);
+ } else {
+ fsm->state = ec_fsm_slave_scan_state_end;
+ }
+#endif
+ return;
+
+end:
+ EC_SLAVE_ERR(slave, "Failed to analyze category data.\n");
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_REGALIAS
+
+/** Slave scan entry function: REGALIAS.
+ */
+void ec_fsm_slave_scan_enter_regalias(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ // read alias from register
+ EC_SLAVE_DBG(slave, 1, "Reading alias from register.\n");
+ ec_datagram_fprd(datagram, slave->station_address, 0x0012, 2);
+ ec_datagram_zero(datagram);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_regalias;
+}
+
+/*****************************************************************************/
+
+/** Slave scan state: REGALIAS.
+ */
+void ec_fsm_slave_scan_state_regalias(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive register alias datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ EC_SLAVE_DBG(slave, 1, "Failed to read register alias.\n");
+ } else {
+ slave->effective_alias = EC_READ_U16(datagram->data);
+ EC_SLAVE_DBG(slave, 1, "Read alias %u from register.\n",
+ slave->effective_alias);
+ }
+
+ if (slave->sii.mailbox_protocols & EC_MBOX_COE) {
+ ec_fsm_slave_scan_enter_preop(fsm);
+ } else {
+ fsm->state = ec_fsm_slave_scan_state_end;
+ }
+}
+
+#endif // defined EC_REGALIAS
+
+/*****************************************************************************/
+
+/** Enter slave scan state PREOP.
+ */
+void ec_fsm_slave_scan_enter_preop(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ uint8_t current_state = slave->current_state & EC_SLAVE_STATE_MASK;
+
+ if (current_state != EC_SLAVE_STATE_PREOP
+ && current_state != EC_SLAVE_STATE_SAFEOP
+ && current_state != EC_SLAVE_STATE_OP) {
+ if (slave->master->debug_level) {
+ char str[EC_STATE_STRING_SIZE];
+ ec_state_string(current_state, str, 0);
+ EC_SLAVE_DBG(slave, 0, "Slave is not in the state"
+ " to do mailbox com (%s), setting to PREOP.\n", str);
+ }
+
+ fsm->state = ec_fsm_slave_scan_state_preop;
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+ ec_fsm_slave_config_start(fsm->fsm_slave_config, slave);
+ ec_fsm_slave_config_exec(fsm->fsm_slave_config);
+ } else {
+ EC_SLAVE_DBG(slave, 1, "Reading mailbox"
+ " sync manager configuration.\n");
+
+ /* Scan current sync manager configuration to get configured mailbox
+ * sizes. */
+ ec_datagram_fprd(fsm->datagram, slave->station_address, 0x0800,
+ EC_SYNC_PAGE_SIZE * 2);
+ fsm->retries = EC_FSM_RETRIES;
+ fsm->state = ec_fsm_slave_scan_state_sync;
+ }
+}
+
+/*****************************************************************************/
+
+/** Slave scan state: PREOP.
+ */
+void ec_fsm_slave_scan_state_preop(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ if (ec_fsm_slave_config_exec(fsm->fsm_slave_config))
+ return;
+
+ if (!ec_fsm_slave_config_success(fsm->fsm_slave_config)) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ return;
+ }
+
+ ec_fsm_slave_scan_enter_pdos(fsm);
+}
+
+/*****************************************************************************/
+
+/** Slave scan state: SYNC.
+ */
+void ec_fsm_slave_scan_state_sync(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_datagram_t *datagram = fsm->datagram;
+ ec_slave_t *slave = fsm->slave;
+ uint16_t tx_offset, tx_size, rx_offset, rx_size;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to receive sync manager"
+ " configuration datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ fsm->slave->error_flag = 1;
+ fsm->state = ec_fsm_slave_scan_state_error;
+ EC_SLAVE_ERR(slave, "Failed to read DL status: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ rx_offset = EC_READ_U16(datagram->data);
+ rx_size = EC_READ_U16(datagram->data + 2);
+ tx_offset = EC_READ_U16(datagram->data + 8);
+ tx_size = EC_READ_U16(datagram->data + 10);
+
+ if (rx_size == 0xffff) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ slave->sii.mailbox_protocols = 0x0000;
+ EC_SLAVE_ERR(slave, "Invalid RX mailbox size (%u) configured."
+ " Disabling mailbox communication.", rx_size);
+ return;
+ }
+
+ if (tx_size == 0xffff) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ slave->sii.mailbox_protocols = 0x0000;
+ EC_SLAVE_ERR(slave, "Invalid TX mailbox size (%u) configured."
+ " Disabling mailbox communication.", tx_size);
+ return;
+ }
+
+ slave->configured_rx_mailbox_offset = rx_offset;
+ slave->configured_rx_mailbox_size = rx_size;
+ slave->configured_tx_mailbox_offset = tx_offset;
+ slave->configured_tx_mailbox_size = tx_size;
+
+ EC_SLAVE_DBG(slave, 1, "Mailbox configuration:\n");
+ EC_SLAVE_DBG(slave, 1, " RX offset=0x%04x size=%u\n",
+ slave->configured_rx_mailbox_offset,
+ slave->configured_rx_mailbox_size);
+ EC_SLAVE_DBG(slave, 1, " TX offset=0x%04x size=%u\n",
+ slave->configured_tx_mailbox_offset,
+ slave->configured_tx_mailbox_size);
+
+ ec_fsm_slave_scan_enter_pdos(fsm);
+}
+
+/*****************************************************************************/
+
+/** Enter slave scan state PDOS.
+ */
+void ec_fsm_slave_scan_enter_pdos(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+
+ EC_SLAVE_DBG(slave, 1, "Scanning PDO assignment and mapping.\n");
+ fsm->state = ec_fsm_slave_scan_state_pdos;
+ ec_fsm_pdo_start_reading(fsm->fsm_pdo, slave);
+ ec_fsm_pdo_exec(fsm->fsm_pdo, fsm->datagram); // execute immediately
+}
+
+/*****************************************************************************/
+
+/** Slave scan state: PDOS.
+ */
+void ec_fsm_slave_scan_state_pdos(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+ if (ec_fsm_pdo_exec(fsm->fsm_pdo, fsm->datagram)) {
+ return;
+ }
+
+ if (!ec_fsm_pdo_success(fsm->fsm_pdo)) {
+ fsm->state = ec_fsm_slave_scan_state_error;
+ return;
+ }
+
+ // reading PDO configuration finished
+ fsm->state = ec_fsm_slave_scan_state_end;
+}
+
+/******************************************************************************
+ * Common state functions
+ *****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_slave_scan_state_error(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_slave_scan_state_end(
+ ec_fsm_slave_scan_t *fsm /**< slave state machine */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_slave_scan.h b/net/ethercat/master/fsm_slave_scan.h
new file mode 100644
index 000000000000..ac63c6f38402
--- /dev/null
+++ b/net/ethercat/master/fsm_slave_scan.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 slave scanning state machine.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SLAVE_SCAN_H__
+#define __EC_FSM_SLAVE_SCAN_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "slave.h"
+#include "fsm_sii.h"
+#include "fsm_change.h"
+#include "fsm_coe.h"
+#include "fsm_pdo.h"
+
+/*****************************************************************************/
+
+/** \see ec_fsm_slave_scan */
+typedef struct ec_fsm_slave_scan ec_fsm_slave_scan_t;
+
+/** Finite state machine for scanning an EtherCAT slave.
+ */
+struct ec_fsm_slave_scan
+{
+ ec_slave_t *slave; /**< Slave the FSM runs on. */
+ ec_datagram_t *datagram; /**< Datagram used in the state machine. */
+ ec_fsm_slave_config_t *fsm_slave_config; /**< Slave configuration state
+ machine to use. */
+ ec_fsm_pdo_t *fsm_pdo; /**< PDO configuration state machine to use. */
+ unsigned int retries; /**< Retries on datagram timeout. */
+
+ void (*state)(ec_fsm_slave_scan_t *); /**< State function. */
+ uint16_t sii_offset; /**< SII offset in words. */
+
+ ec_fsm_sii_t fsm_sii; /**< SII state machine. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_slave_scan_init(ec_fsm_slave_scan_t *, ec_datagram_t *,
+ ec_fsm_slave_config_t *, ec_fsm_pdo_t *);
+void ec_fsm_slave_scan_clear(ec_fsm_slave_scan_t *);
+
+void ec_fsm_slave_scan_start(ec_fsm_slave_scan_t *, ec_slave_t *);
+
+int ec_fsm_slave_scan_exec(ec_fsm_slave_scan_t *);
+int ec_fsm_slave_scan_success(const ec_fsm_slave_scan_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/fsm_soe.c b/net/ethercat/master/fsm_soe.c
new file mode 100644
index 000000000000..00d39faffe7f
--- /dev/null
+++ b/net/ethercat/master/fsm_soe.c
@@ -0,0 +1,846 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2006-2020 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 SoE state machines.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "master.h"
+#include "mailbox.h"
+#include "fsm_soe.h"
+
+/*****************************************************************************/
+
+/** Mailbox type for SoE.
+ */
+#define EC_MBOX_TYPE_SOE 0x05
+
+/** SoE operations
+ */
+enum ec_soe_opcodes {
+ OPCODE_READ_REQUEST = 0x01, /**< Read request. */
+ OPCODE_READ_RESPONSE = 0x02, /**< Read response. */
+ OPCODE_WRITE_REQUEST = 0x03, /**< Write request. */
+ OPCODE_WRITE_RESPONSE = 0x04 /**< Write response. */
+};
+
+/** Size of all SoE headers.
+ */
+#define EC_SOE_SIZE 0x04
+
+/** SoE header size.
+ */
+#define EC_SOE_HEADER_SIZE (EC_MBOX_HEADER_SIZE + EC_SOE_SIZE)
+
+/** SoE response timeout [ms].
+ */
+#define EC_SOE_RESPONSE_TIMEOUT 1000
+
+/*****************************************************************************/
+
+void ec_fsm_soe_read_start(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_read_request(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_read_check(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_read_response(ec_fsm_soe_t *, ec_datagram_t *);
+
+void ec_fsm_soe_write_start(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_write_request(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_write_check(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_write_response(ec_fsm_soe_t *, ec_datagram_t *);
+
+void ec_fsm_soe_end(ec_fsm_soe_t *, ec_datagram_t *);
+void ec_fsm_soe_error(ec_fsm_soe_t *, ec_datagram_t *);
+
+/*****************************************************************************/
+
+extern const ec_code_msg_t soe_error_codes[];
+
+/*****************************************************************************/
+
+/** Outputs an SoE error code.
+*/
+void ec_print_soe_error(const ec_slave_t *slave, uint16_t error_code)
+{
+ const ec_code_msg_t *error_msg;
+
+ for (error_msg = soe_error_codes; error_msg->code; error_msg++) {
+ if (error_msg->code == error_code) {
+ EC_SLAVE_ERR(slave, "SoE error 0x%04X: \"%s\".\n",
+ error_msg->code, error_msg->message);
+ return;
+ }
+ }
+
+ EC_SLAVE_ERR(slave, "Unknown SoE error 0x%04X.\n", error_code);
+}
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_fsm_soe_init(
+ ec_fsm_soe_t *fsm /**< finite state machine */
+ )
+{
+ fsm->state = NULL;
+ fsm->datagram = NULL;
+ fsm->fragment_size = 0;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_fsm_soe_clear(
+ ec_fsm_soe_t *fsm /**< finite state machine */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** Starts to transfer an IDN to/from a slave.
+ */
+void ec_fsm_soe_transfer(
+ ec_fsm_soe_t *fsm, /**< State machine. */
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ ec_soe_request_t *request /**< SoE request. */
+ )
+{
+ fsm->slave = slave;
+ fsm->request = request;
+
+ if (request->dir == EC_DIR_OUTPUT) {
+ fsm->state = ec_fsm_soe_write_start;
+ } else {
+ fsm->state = ec_fsm_soe_read_start;
+ }
+}
+
+/*****************************************************************************/
+
+/** Executes the current state of the state machine.
+ *
+ * \return 1 if the datagram was used, else 0.
+ */
+int ec_fsm_soe_exec(
+ ec_fsm_soe_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_soe_end && fsm->state != ec_fsm_soe_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_soe_success(const ec_fsm_soe_t *fsm /**< Finite state machine */)
+{
+ return fsm->state == ec_fsm_soe_end;
+}
+
+/*****************************************************************************/
+
+/** Output information about a failed SoE transfer.
+ */
+void ec_fsm_soe_print_error(ec_fsm_soe_t *fsm /**< Finite state machine */)
+{
+ ec_soe_request_t *request = fsm->request;
+
+ EC_SLAVE_ERR(fsm->slave, "");
+
+ if (request->dir == EC_DIR_OUTPUT) {
+ printk("Writing");
+ } else {
+ printk("Reading");
+ }
+
+ printk(" IDN 0x%04X failed.\n", request->idn);
+}
+
+/******************************************************************************
+ * SoE read state machine
+ *****************************************************************************/
+
+/** Prepare a read operation.
+ *
+ * \return 0 on success, otherwise a negative error code.
+ */
+int ec_fsm_soe_prepare_read(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ uint8_t *data;
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ ec_soe_request_t *request = fsm->request;
+
+ data = ec_slave_mbox_prepare_send(slave, datagram, EC_MBOX_TYPE_SOE,
+ EC_SOE_SIZE);
+ if (IS_ERR(data)) {
+ return PTR_ERR(data);
+ }
+
+ EC_WRITE_U8(data, OPCODE_READ_REQUEST | (request->drive_no & 0x07) << 5);
+ EC_WRITE_U8(data + 1, 1 << 6); // request value
+ EC_WRITE_U16(data + 2, request->idn);
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "SSC read request:\n");
+ ec_print_data(data, EC_SOE_SIZE);
+ }
+
+ fsm->request->jiffies_sent = jiffies;
+ fsm->state = ec_fsm_soe_read_request;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** SoE state: READ START.
+ */
+void ec_fsm_soe_read_start(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *request = fsm->request;
+
+ EC_SLAVE_DBG(slave, 1, "Reading IDN 0x%04X of drive %u.\n", request->idn,
+ request->drive_no);
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_SOE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support SoE!\n");
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ request->data_size = 0;
+ fsm->retries = EC_FSM_RETRIES;
+
+ if (ec_fsm_soe_prepare_read(fsm, datagram)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ }
+}
+
+/*****************************************************************************/
+
+/** SoE state: READ REQUEST.
+ */
+void ec_fsm_soe_read_request(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ unsigned long diff_ms;
+
+ if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ if (ec_fsm_soe_prepare_read(fsm, datagram)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ }
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE read request: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ;
+
+ if (fsm->datagram->working_counter != 1) {
+ if (!fsm->datagram->working_counter) {
+ if (diff_ms < EC_SOE_RESPONSE_TIMEOUT) {
+ // no response; send request datagram again
+ if (ec_fsm_soe_prepare_read(fsm, datagram)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ }
+ return;
+ }
+ }
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE read request"
+ " failed after %lu ms: ", diff_ms);
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ 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_soe_read_check;
+}
+
+/*****************************************************************************/
+
+/** CoE state: READ CHECK.
+ */
+void ec_fsm_soe_read_check(
+ ec_fsm_soe_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_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE mailbox check datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE mailbox check"
+ " datagram failed: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ 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_SOE_RESPONSE_TIMEOUT) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting for"
+ " read response.\n", diff_ms);
+ ec_fsm_soe_print_error(fsm);
+ 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_soe_read_response;
+}
+
+/*****************************************************************************/
+
+/** SoE state: READ RESPONSE.
+ */
+void ec_fsm_soe_read_response(
+ ec_fsm_soe_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, header, opcode, incomplete, error_flag,
+ value_included;
+ size_t rec_size, data_size;
+ ec_soe_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_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE read response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE read response failed: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ data = ec_slave_mbox_fetch(slave, fsm->datagram, &mbox_prot, &rec_size);
+ if (IS_ERR(data)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "SSC read response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_SOE) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (rec_size < EC_SOE_SIZE) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Received currupted SoE read response"
+ " (%zu bytes)!\n", rec_size);
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ header = EC_READ_U8(data);
+ opcode = header & 0x7;
+ incomplete = (header >> 3) & 1;
+ error_flag = (header >> 4) & 1;
+
+ if (opcode != OPCODE_READ_RESPONSE) {
+ EC_SLAVE_ERR(slave, "Received no read response (opcode %x).\n",
+ opcode);
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ return;
+ }
+
+ if (error_flag) {
+ req->error_code = EC_READ_U16(data + rec_size - 2);
+ EC_SLAVE_ERR(slave, "Received error response:\n");
+ ec_print_soe_error(slave, req->error_code);
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ return;
+ } else {
+ req->error_code = 0x0000;
+ }
+
+ value_included = (EC_READ_U8(data + 1) >> 6) & 1;
+ if (!value_included) {
+ EC_SLAVE_ERR(slave, "No value included!\n");
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ return;
+ }
+
+ data_size = rec_size - EC_SOE_SIZE;
+ if (ec_soe_request_append_data(req,
+ data + EC_SOE_SIZE, data_size)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (incomplete) {
+ EC_SLAVE_DBG(slave, 1, "SoE data incomplete. Waiting for fragment"
+ " at offset %zu.\n", req->data_size);
+ 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_soe_read_check;
+ } else {
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "IDN data:\n");
+ ec_print_data(req->data, req->data_size);
+ }
+
+ fsm->state = ec_fsm_soe_end; // success
+ }
+}
+
+/******************************************************************************
+ * SoE write state machine
+ *****************************************************************************/
+
+/** Write next fragment.
+ */
+void ec_fsm_soe_write_next_fragment(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ ec_soe_request_t *req = fsm->request;
+ uint8_t incomplete, *data;
+ size_t max_fragment_size, remaining_size;
+ uint16_t fragments_left;
+
+ remaining_size = req->data_size - fsm->offset;
+ max_fragment_size = slave->configured_rx_mailbox_size - EC_SOE_HEADER_SIZE;
+ incomplete = remaining_size > max_fragment_size;
+ fsm->fragment_size = incomplete ? max_fragment_size : remaining_size;
+ fragments_left = remaining_size / fsm->fragment_size - 1;
+ if (remaining_size % fsm->fragment_size) {
+ fragments_left++;
+ }
+
+ data = ec_slave_mbox_prepare_send(slave, datagram, EC_MBOX_TYPE_SOE,
+ EC_SOE_SIZE + fsm->fragment_size);
+ if (IS_ERR(data)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ EC_WRITE_U8(data, OPCODE_WRITE_REQUEST | incomplete << 3 |
+ (req->drive_no & 0x07) << 5);
+ EC_WRITE_U8(data + 1, 1 << 6); // only value included
+ EC_WRITE_U16(data + 2, incomplete ? fragments_left : req->idn);
+ memcpy(data + EC_SOE_SIZE, req->data + fsm->offset, fsm->fragment_size);
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "SSC write request:\n");
+ ec_print_data(data, EC_SOE_SIZE + fsm->fragment_size);
+ }
+
+ fsm->state = ec_fsm_soe_write_request;
+}
+
+/*****************************************************************************/
+
+/** SoE state: WRITE START.
+ */
+void ec_fsm_soe_write_start(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_soe_request_t *req = fsm->request;
+
+ EC_SLAVE_DBG(slave, 1, "Writing IDN 0x%04X of drive %u (%zu byte).\n",
+ req->idn, req->drive_no, req->data_size);
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_SOE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support SoE!\n");
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (slave->configured_rx_mailbox_size <= EC_SOE_HEADER_SIZE) {
+ EC_SLAVE_ERR(slave, "Mailbox size (%u) too small for SoE write.\n",
+ slave->configured_rx_mailbox_size);
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ fsm->offset = 0;
+ fsm->retries = EC_FSM_RETRIES;
+ ec_fsm_soe_write_next_fragment(fsm, datagram);
+ req->jiffies_sent = jiffies;
+}
+
+/*****************************************************************************/
+
+/** SoE state: WRITE REQUEST.
+ */
+void ec_fsm_soe_write_request(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ unsigned long diff_ms;
+
+ if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ ec_fsm_soe_write_next_fragment(fsm, datagram);
+ return;
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE write request: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ diff_ms = (jiffies - fsm->request->jiffies_sent) * 1000 / HZ;
+
+ if (fsm->datagram->working_counter != 1) {
+ if (!fsm->datagram->working_counter) {
+ if (diff_ms < EC_SOE_RESPONSE_TIMEOUT) {
+ // no response; send request datagram again
+ ec_fsm_soe_write_next_fragment(fsm, datagram);
+ return;
+ }
+ }
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE write request"
+ " failed after %lu ms: ", diff_ms);
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ // fragment successfully sent
+ fsm->offset += fsm->fragment_size;
+
+ if (fsm->offset < fsm->request->data_size) {
+ // next fragment
+ fsm->retries = EC_FSM_RETRIES;
+ ec_fsm_soe_write_next_fragment(fsm, datagram);
+ fsm->request->jiffies_sent = jiffies;
+ } else {
+ // all fragments sent; query response
+ 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_soe_write_check;
+ }
+}
+
+/*****************************************************************************/
+
+/** CoE state: WRITE CHECK.
+ */
+void ec_fsm_soe_write_check(
+ ec_fsm_soe_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_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE write request datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE write request datagram: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(fsm->datagram)) {
+ unsigned long diff_ms =
+ (datagram->jiffies_received - fsm->jiffies_start) * 1000 / HZ;
+ if (diff_ms >= EC_SOE_RESPONSE_TIMEOUT) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Timeout after %lu ms while waiting"
+ " for write response.\n", diff_ms);
+ ec_fsm_soe_print_error(fsm);
+ 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_soe_write_response;
+}
+
+/*****************************************************************************/
+
+/** SoE state: WRITE RESPONSE.
+ */
+void ec_fsm_soe_write_response(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+ ec_slave_t *slave = fsm->slave;
+ ec_master_t *master = slave->master;
+ ec_soe_request_t *req = fsm->request;
+ uint8_t *data, mbox_prot, opcode, error_flag;
+ uint16_t idn;
+ size_t rec_size;
+
+ if (fsm->datagram->state == EC_DATAGRAM_TIMED_OUT && fsm->retries--) {
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ return; // FIXME: request again?
+ }
+
+ if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Failed to receive SoE write"
+ " response datagram: ");
+ ec_datagram_print_state(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (fsm->datagram->working_counter != 1) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Reception of SoE write response failed: ");
+ ec_datagram_print_wc_error(fsm->datagram);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ data = ec_slave_mbox_fetch(slave, fsm->datagram, &mbox_prot, &rec_size);
+ if (IS_ERR(data)) {
+ fsm->state = ec_fsm_soe_error;
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "SSC write response:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_SOE) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Received mailbox protocol 0x%02X as response.\n",
+ mbox_prot);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ if (rec_size < EC_SOE_SIZE) {
+ fsm->state = ec_fsm_soe_error;
+ EC_SLAVE_ERR(slave, "Received corrupted SoE write response"
+ " (%zu bytes)!\n", rec_size);
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ return;
+ }
+
+ opcode = EC_READ_U8(data) & 0x7;
+ if (opcode != OPCODE_WRITE_RESPONSE) {
+ EC_SLAVE_ERR(slave, "Received no write response"
+ " (opcode %x).\n", opcode);
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ return;
+ }
+
+ idn = EC_READ_U16(data + 2);
+ if (idn != req->idn) {
+ EC_SLAVE_ERR(slave, "Received response for"
+ " wrong IDN 0x%04x.\n", idn);
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ return;
+ }
+
+ error_flag = (EC_READ_U8(data) >> 4) & 1;
+ if (error_flag) {
+ if (rec_size < EC_SOE_SIZE + 2) {
+ EC_SLAVE_ERR(slave, "Received corrupted error response"
+ " - error flag set, but received size is %zu.\n",
+ rec_size);
+ } else {
+ req->error_code = EC_READ_U16(data + EC_SOE_SIZE);
+ EC_SLAVE_ERR(slave, "Received error response:\n");
+ ec_print_soe_error(slave, req->error_code);
+ }
+ ec_print_data(data, rec_size);
+ ec_fsm_soe_print_error(fsm);
+ fsm->state = ec_fsm_soe_error;
+ } else {
+ req->error_code = 0x0000;
+ fsm->state = ec_fsm_soe_end; // success
+ }
+}
+
+/*****************************************************************************/
+
+/** State: ERROR.
+ */
+void ec_fsm_soe_error(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
+
+/** State: END.
+ */
+void ec_fsm_soe_end(
+ ec_fsm_soe_t *fsm, /**< finite state machine */
+ ec_datagram_t *datagram /**< Datagram to use. */
+ )
+{
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/fsm_soe.h b/net/ethercat/master/fsm_soe.h
new file mode 100644
index 000000000000..99aa204670cf
--- /dev/null
+++ b/net/ethercat/master/fsm_soe.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CoE state machines.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_FSM_SOE_H__
+#define __EC_FSM_SOE_H__
+
+#include "globals.h"
+#include "datagram.h"
+#include "slave.h"
+#include "soe_request.h"
+
+/*****************************************************************************/
+
+typedef struct ec_fsm_soe ec_fsm_soe_t; /**< \see ec_fsm_soe */
+
+/** Finite state machines for the Sercos over EtherCAT protocol.
+ */
+struct ec_fsm_soe {
+ ec_slave_t *slave; /**< slave the FSM runs on */
+ unsigned int retries; /**< retries upon datagram timeout */
+
+ void (*state)(ec_fsm_soe_t *, ec_datagram_t *); /**< CoE state function */
+ ec_datagram_t *datagram; /**< Datagram used in the previous step. */
+ unsigned long jiffies_start; /**< Timestamp. */
+ ec_soe_request_t *request; /**< SoE request */
+ off_t offset; /**< IDN data offset during fragmented write. */
+ size_t fragment_size; /**< Size of the current fragment. */
+};
+
+/*****************************************************************************/
+
+void ec_fsm_soe_init(ec_fsm_soe_t *);
+void ec_fsm_soe_clear(ec_fsm_soe_t *);
+
+void ec_fsm_soe_transfer(ec_fsm_soe_t *, ec_slave_t *, ec_soe_request_t *);
+
+int ec_fsm_soe_exec(ec_fsm_soe_t *, ec_datagram_t *);
+int ec_fsm_soe_success(const ec_fsm_soe_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/globals.h b/net/ethercat/master/globals.h
new file mode 100644
index 000000000000..840a160cf4ca
--- /dev/null
+++ b/net/ethercat/master/globals.h
@@ -0,0 +1,317 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ * Global definitions and macros.
+ */
+
+/*****************************************************************************/
+
+#ifndef __EC_MASTER_GLOBALS_H__
+#define __EC_MASTER_GLOBALS_H__
+
+#include "../globals.h"
+#include "../include/ecrt.h"
+
+/******************************************************************************
+ * EtherCAT master
+ *****************************************************************************/
+
+/** Datagram timeout in microseconds. */
+#define EC_IO_TIMEOUT 500
+
+/** SDO injection timeout in microseconds. */
+#define EC_SDO_INJECTION_TIMEOUT 10000
+
+/** Time to send a byte in nanoseconds.
+ *
+ * t_ns = 1 / (100 MBit/s / 8 bit/byte) = 80 ns/byte
+ */
+#define EC_BYTE_TRANSMISSION_TIME_NS 80
+
+/** Number of state machine retries on datagram timeout. */
+#define EC_FSM_RETRIES 3
+
+/** Seconds to wait before fetching SDO dictionary
+ after slave entered PREOP state. */
+#define EC_WAIT_SDO_DICT 3
+
+/** Minimum size of a buffer used with ec_state_string(). */
+#define EC_STATE_STRING_SIZE 32
+
+/** Maximum SII size in words, to avoid infinite reading. */
+#define EC_MAX_SII_SIZE 4096
+
+/** Number of statistic rate intervals to maintain. */
+#define EC_RATE_COUNT 3
+
+/******************************************************************************
+ * EtherCAT protocol
+ *****************************************************************************/
+
+/** Size of an EtherCAT frame header. */
+#define EC_FRAME_HEADER_SIZE 2
+
+/** Size of an EtherCAT datagram header. */
+#define EC_DATAGRAM_HEADER_SIZE 10
+
+/** Size of an EtherCAT datagram footer. */
+#define EC_DATAGRAM_FOOTER_SIZE 2
+
+/** Size of the EtherCAT address field. */
+#define EC_ADDR_LEN 4
+
+/** Resulting maximum data size of a single datagram in a frame. */
+#define EC_MAX_DATA_SIZE (ETH_DATA_LEN - EC_FRAME_HEADER_SIZE \
+ - EC_DATAGRAM_HEADER_SIZE - EC_DATAGRAM_FOOTER_SIZE)
+
+/** Mailbox header size. */
+#define EC_MBOX_HEADER_SIZE 6
+
+/** Word offset of first SII category. */
+#define EC_FIRST_SII_CATEGORY_OFFSET 0x40
+
+/** Size of a sync manager configuration page. */
+#define EC_SYNC_PAGE_SIZE 8
+
+/** Maximum number of FMMUs per slave. */
+#define EC_MAX_FMMUS 16
+
+/** Size of an FMMU configuration page. */
+#define EC_FMMU_PAGE_SIZE 16
+
+/** Number of DC sync signals. */
+#define EC_SYNC_SIGNAL_COUNT 2
+
+/** Size of the datagram description string.
+ *
+ * This is also used as the maximum lenth of EoE device names.
+ **/
+#define EC_DATAGRAM_NAME_SIZE 20
+
+/** Slave state mask.
+ *
+ * Apply this mask to a slave state byte to get the slave state without
+ * the error flag.
+ */
+#define EC_SLAVE_STATE_MASK 0x0F
+
+/** State of an EtherCAT slave.
+ */
+typedef enum {
+ EC_SLAVE_STATE_UNKNOWN = 0x00,
+ /**< unknown state */
+ EC_SLAVE_STATE_INIT = 0x01,
+ /**< INIT state (no mailbox communication, no IO) */
+ EC_SLAVE_STATE_PREOP = 0x02,
+ /**< PREOP state (mailbox communication, no IO) */
+ EC_SLAVE_STATE_BOOT = 0x03,
+ /**< Bootstrap state (mailbox communication, firmware update) */
+ EC_SLAVE_STATE_SAFEOP = 0x04,
+ /**< SAFEOP (mailbox communication and input update) */
+ EC_SLAVE_STATE_OP = 0x08,
+ /**< OP (mailbox communication and input/output update) */
+ EC_SLAVE_STATE_ACK_ERR = 0x10
+ /**< Acknowledge/Error bit (no actual state) */
+} ec_slave_state_t;
+
+/** Supported mailbox protocols.
+ */
+enum {
+ EC_MBOX_AOE = 0x01, /**< ADS over EtherCAT */
+ EC_MBOX_EOE = 0x02, /**< Ethernet over EtherCAT */
+ EC_MBOX_COE = 0x04, /**< CANopen over EtherCAT */
+ EC_MBOX_FOE = 0x08, /**< File-Access over EtherCAT */
+ EC_MBOX_SOE = 0x10, /**< Servo-Profile over EtherCAT */
+ EC_MBOX_VOE = 0x20 /**< Vendor specific */
+};
+
+/** Slave information interface CANopen over EtherCAT details flags.
+ */
+typedef struct {
+ uint8_t enable_sdo : 1; /**< Enable SDO access. */
+ uint8_t enable_sdo_info : 1; /**< SDO information service available. */
+ uint8_t enable_pdo_assign : 1; /**< PDO mapping configurable. */
+ uint8_t enable_pdo_configuration : 1; /**< PDO configuration possible. */
+ uint8_t enable_upload_at_startup : 1; /**< ?. */
+ uint8_t enable_sdo_complete_access : 1; /**< Complete access possible. */
+} ec_sii_coe_details_t;
+
+/** Slave information interface general flags.
+ */
+typedef struct {
+ uint8_t enable_safeop : 1; /**< ?. */
+ uint8_t enable_not_lrw : 1; /**< Slave does not support LRW. */
+} ec_sii_general_flags_t;
+
+/** EtherCAT slave distributed clocks range.
+ */
+typedef enum {
+ EC_DC_32, /**< 32 bit. */
+ EC_DC_64 /*< 64 bit for system time, system time offset and
+ port 0 receive time. */
+} ec_slave_dc_range_t;
+
+/** EtherCAT slave sync signal configuration.
+ */
+typedef struct {
+ uint32_t cycle_time; /**< Cycle time [ns]. */
+ int32_t shift_time; /**< Shift time [ns]. */
+} ec_sync_signal_t;
+
+/** Access states for SDO entries.
+ *
+ * The access rights are managed per AL state.
+ */
+enum {
+ EC_SDO_ENTRY_ACCESS_PREOP, /**< Access rights in PREOP. */
+ EC_SDO_ENTRY_ACCESS_SAFEOP, /**< Access rights in SAFEOP. */
+ EC_SDO_ENTRY_ACCESS_OP, /**< Access rights in OP. */
+ EC_SDO_ENTRY_ACCESS_COUNT /**< Number of states. */
+};
+
+/** Master devices.
+ */
+typedef enum {
+ EC_DEVICE_MAIN, /**< Main device. */
+ EC_DEVICE_BACKUP /**< Backup device */
+} ec_device_index_t;
+
+extern const char *ec_device_names[2]; // only main and backup!
+
+/*****************************************************************************/
+
+/** Convenience macro for printing EtherCAT-specific information to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT: ".
+ *
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_INFO(fmt, args...) \
+ printk(KERN_INFO "EtherCAT: " fmt, ##args)
+
+/** Convenience macro for printing EtherCAT-specific errors to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT ERROR: ".
+ *
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_ERR(fmt, args...) \
+ printk(KERN_ERR "EtherCAT ERROR: " fmt, ##args)
+
+/** Convenience macro for printing EtherCAT-specific warnings to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT WARNING: ".
+ *
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_WARN(fmt, args...) \
+ printk(KERN_WARNING "EtherCAT WARNING: " fmt, ##args)
+
+/** Convenience macro for printing EtherCAT debug messages to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT DEBUG: ".
+ *
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_DBG(fmt, args...) \
+ printk(KERN_DEBUG "EtherCAT DEBUG: " fmt, ##args)
+
+/*****************************************************************************/
+
+/** Absolute value.
+ */
+#define EC_ABS(X) ((X) >= 0 ? (X) : -(X))
+
+/*****************************************************************************/
+
+extern char *ec_master_version_str;
+
+/*****************************************************************************/
+
+unsigned int ec_master_count(void);
+void ec_print_data(const uint8_t *, size_t);
+void ec_print_data_diff(const uint8_t *, const uint8_t *, size_t);
+size_t ec_state_string(uint8_t, char *, uint8_t);
+ssize_t ec_mac_print(const uint8_t *, char *);
+int ec_mac_is_zero(const uint8_t *);
+
+ec_master_t *ecrt_request_master_err(unsigned int);
+
+/*****************************************************************************/
+
+/** Code/Message pair.
+ *
+ * Some EtherCAT datagrams support reading a status code to display a certain
+ * message. This type allows to map a code to a message string.
+ */
+typedef struct {
+ uint32_t code; /**< Code. */
+ const char *message; /**< Message belonging to \a code. */
+} ec_code_msg_t;
+
+/*****************************************************************************/
+
+/** Generic request state.
+ *
+ * \attention If ever changing this, please be sure to adjust the \a
+ * state_table in master/sdo_request.c.
+ */
+typedef enum {
+ EC_INT_REQUEST_INIT,
+ EC_INT_REQUEST_QUEUED,
+ EC_INT_REQUEST_BUSY,
+ EC_INT_REQUEST_SUCCESS,
+ EC_INT_REQUEST_FAILURE
+} ec_internal_request_state_t;
+
+/*****************************************************************************/
+
+extern const ec_request_state_t ec_request_state_translation_table[];
+
+/*****************************************************************************/
+
+/** Origin type.
+ */
+typedef enum {
+ EC_ORIG_INTERNAL, /**< Internal. */
+ EC_ORIG_EXTERNAL /**< External. */
+} ec_origin_t;
+
+/*****************************************************************************/
+
+typedef struct ec_slave ec_slave_t; /**< \see ec_slave. */
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/ioctl.c b/net/ethercat/master/ioctl.c
new file mode 100644
index 000000000000..69fb35527a8a
--- /dev/null
+++ b/net/ethercat/master/ioctl.c
@@ -0,0 +1,4646 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master character device.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "master.h"
+#include "slave_config.h"
+#include "voe_handler.h"
+#include "ethernet.h"
+#include "ioctl.h"
+
+/** Set to 1 to enable ioctl() latency tracing.
+ *
+ * Requires CPU timestamp counter!
+ */
+#define DEBUG_LATENCY 0
+
+/** Optional compiler attributes fo ioctl() functions.
+ */
+#if 0
+#define ATTRIBUTES __attribute__ ((__noinline__))
+#else
+#define ATTRIBUTES
+#endif
+
+/*****************************************************************************/
+
+/** Copies a string to an ioctl structure.
+ */
+static void ec_ioctl_strcpy(
+ char *target, /**< Target. */
+ const char *source /**< Source. */
+ )
+{
+ if (source) {
+ strncpy(target, source, EC_IOCTL_STRING_SIZE);
+ target[EC_IOCTL_STRING_SIZE - 1] = 0;
+ } else {
+ target[0] = 0;
+ }
+}
+
+/*****************************************************************************/
+
+/** Get module information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_module(
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_module_t data;
+
+ data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC;
+ data.master_count = ec_master_count();
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get master information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_master_t io;
+ unsigned int dev_idx, j;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ io.slave_count = master->slave_count;
+ io.config_count = ec_master_config_count(master);
+ io.domain_count = ec_master_domain_count(master);
+#ifdef EC_EOE
+ io.eoe_handler_count = ec_master_eoe_handler_count(master);
+#endif
+ io.phase = (uint8_t) master->phase;
+ io.active = (uint8_t) master->active;
+ io.scan_busy = master->scan_busy;
+
+ up(&master->master_sem);
+
+ if (down_interruptible(&master->device_sem)) {
+ return -EINTR;
+ }
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(master); dev_idx++) {
+ ec_device_t *device = &master->devices[dev_idx];
+
+ if (device->dev) {
+ memcpy(io.devices[dev_idx].address, device->dev->dev_addr,
+ ETH_ALEN);
+ } else {
+ memcpy(io.devices[dev_idx].address, master->macs[dev_idx],
+ ETH_ALEN);
+ }
+ io.devices[dev_idx].attached = device->dev ? 1 : 0;
+ io.devices[dev_idx].link_state = device->link_state ? 1 : 0;
+ io.devices[dev_idx].tx_count = device->tx_count;
+ io.devices[dev_idx].rx_count = device->rx_count;
+ io.devices[dev_idx].tx_bytes = device->tx_bytes;
+ io.devices[dev_idx].rx_bytes = device->rx_bytes;
+ io.devices[dev_idx].tx_errors = device->tx_errors;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ io.devices[dev_idx].tx_frame_rates[j] =
+ device->tx_frame_rates[j];
+ io.devices[dev_idx].rx_frame_rates[j] =
+ device->rx_frame_rates[j];
+ io.devices[dev_idx].tx_byte_rates[j] =
+ device->tx_byte_rates[j];
+ io.devices[dev_idx].rx_byte_rates[j] =
+ device->rx_byte_rates[j];
+ }
+ }
+ io.num_devices = ec_master_num_devices(master);
+
+ io.tx_count = master->device_stats.tx_count;
+ io.rx_count = master->device_stats.rx_count;
+ io.tx_bytes = master->device_stats.tx_bytes;
+ io.rx_bytes = master->device_stats.rx_bytes;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ io.tx_frame_rates[j] =
+ master->device_stats.tx_frame_rates[j];
+ io.rx_frame_rates[j] =
+ master->device_stats.rx_frame_rates[j];
+ io.tx_byte_rates[j] =
+ master->device_stats.tx_byte_rates[j];
+ io.rx_byte_rates[j] =
+ master->device_stats.rx_byte_rates[j];
+ io.loss_rates[j] =
+ master->device_stats.loss_rates[j];
+ }
+
+ up(&master->device_sem);
+
+ io.app_time = master->app_time;
+ io.dc_ref_time = master->dc_ref_time;
+ io.ref_clock =
+ master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_t data;
+ const ec_slave_t *slave;
+ int i;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.position);
+ return -EINVAL;
+ }
+
+ data.device_index = slave->device_index;
+ data.vendor_id = slave->sii.vendor_id;
+ data.product_code = slave->sii.product_code;
+ data.revision_number = slave->sii.revision_number;
+ data.serial_number = slave->sii.serial_number;
+ data.alias = slave->effective_alias;
+ data.boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset;
+ data.boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size;
+ data.boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset;
+ data.boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size;
+ data.std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset;
+ data.std_rx_mailbox_size = slave->sii.std_rx_mailbox_size;
+ data.std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset;
+ data.std_tx_mailbox_size = slave->sii.std_tx_mailbox_size;
+ data.mailbox_protocols = slave->sii.mailbox_protocols;
+ data.has_general_category = slave->sii.has_general;
+ data.coe_details = slave->sii.coe_details;
+ data.general_flags = slave->sii.general_flags;
+ data.current_on_ebus = slave->sii.current_on_ebus;
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ data.ports[i].desc = slave->ports[i].desc;
+ data.ports[i].link.link_up = slave->ports[i].link.link_up;
+ data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed;
+ data.ports[i].link.signal_detected =
+ slave->ports[i].link.signal_detected;
+ data.ports[i].receive_time = slave->ports[i].receive_time;
+ if (slave->ports[i].next_slave) {
+ data.ports[i].next_slave =
+ slave->ports[i].next_slave->ring_position;
+ } else {
+ data.ports[i].next_slave = 0xffff;
+ }
+ data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc;
+ }
+ data.fmmu_bit = slave->base_fmmu_bit_operation;
+ data.dc_supported = slave->base_dc_supported;
+ data.dc_range = slave->base_dc_range;
+ data.has_dc_system_time = slave->has_dc_system_time;
+ data.transmission_delay = slave->transmission_delay;
+ data.al_state = slave->current_state;
+ data.error_flag = slave->error_flag;
+
+ data.sync_count = slave->sii.sync_count;
+ data.sdo_count = ec_slave_sdo_count(slave);
+ data.sii_nwords = slave->sii_nwords;
+ ec_ioctl_strcpy(data.group, slave->sii.group);
+ ec_ioctl_strcpy(data.image, slave->sii.image);
+ ec_ioctl_strcpy(data.order, slave->sii.order);
+ ec_ioctl_strcpy(data.name, slave->sii.name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+
+ data.physical_start_address = sync->physical_start_address;
+ data.default_size = sync->default_length;
+ data.control_register = sync->control_register;
+ data.enable = sync->enable;
+ data.pdo_count = ec_pdo_list_count(&sync->pdos);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager PDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_pdo_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+ const ec_pdo_t *pdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sync->pdos, data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
+ "position %u!\n", data.sync_index, data.pdo_pos);
+ return -EINVAL;
+ }
+
+ data.index = pdo->index;
+ data.entry_count = ec_pdo_entry_count(pdo);
+ ec_ioctl_strcpy(data.name, pdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager PDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_pdo_entry_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+ const ec_pdo_t *pdo;
+ const ec_pdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sync->pdos, data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
+ "position %u!\n", data.sync_index, data.pdo_pos);
+ return -EINVAL;
+ }
+
+ if (!(entry = ec_pdo_find_entry_by_pos_const(
+ pdo, data.entry_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "PDO 0x%04X does not contain an entry with "
+ "position %u!\n", data.pdo_pos, data.entry_pos);
+ return -EINVAL;
+ }
+
+ data.index = entry->index;
+ data.subindex = entry->subindex;
+ data.bit_length = entry->bit_length;
+ ec_ioctl_strcpy(data.name, entry->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_t data;
+ const ec_domain_t *domain;
+ unsigned int dev_idx;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n", data.index);
+ return -EINVAL;
+ }
+
+ data.data_size = domain->data_size;
+ data.logical_base_address = domain->logical_base_address;
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ data.working_counter[dev_idx] = domain->working_counter[dev_idx];
+ }
+ data.expected_working_counter = domain->expected_working_counter;
+ data.fmmu_count = ec_domain_fmmu_count(domain);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain FMMU information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_fmmu(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_fmmu_t data;
+ const ec_domain_t *domain;
+ const ec_fmmu_config_t *fmmu;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n",
+ data.domain_index);
+ return -EINVAL;
+ }
+
+ if (!(fmmu = ec_domain_find_fmmu(domain, data.fmmu_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u has less than %u"
+ " fmmu configurations.\n",
+ data.domain_index, data.fmmu_index + 1);
+ return -EINVAL;
+ }
+
+ data.slave_config_alias = fmmu->sc->alias;
+ data.slave_config_position = fmmu->sc->position;
+ data.sync_index = fmmu->sync_index;
+ data.dir = fmmu->dir;
+ data.logical_address = fmmu->logical_start_address;
+ data.data_size = fmmu->data_size;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_data_t data;
+ const ec_domain_t *domain;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n",
+ data.domain_index);
+ return -EINVAL;
+ }
+
+ if (domain->data_size != data.data_size) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Data size mismatch %u/%zu!\n",
+ data.data_size, domain->data_size);
+ return -EFAULT;
+ }
+
+ if (copy_to_user((void __user *) data.target, domain->data,
+ domain->data_size)) {
+ up(&master->master_sem);
+ return -EFAULT;
+ }
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set master debug level.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_debug(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ return ec_master_debug_level(master, (unsigned long) arg);
+}
+
+/*****************************************************************************/
+
+/** Issue a bus scan.
+ *
+ * \return Always zero (success).
+ */
+static ATTRIBUTES int ec_ioctl_master_rescan(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ master->fsm.rescan_required = 1;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set slave state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_state_t data;
+ ec_slave_t *slave;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ ec_slave_request_state(slave, data.al_state);
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave SDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_t data;
+ const ec_slave_t *slave;
+ const ec_sdo_t *sdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (!(sdo = ec_slave_get_sdo_by_pos_const(
+ slave, data.sdo_position))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", data.sdo_position);
+ return -EINVAL;
+ }
+
+ data.sdo_index = sdo->index;
+ data.max_subindex = sdo->max_subindex;
+ ec_ioctl_strcpy(data.name, sdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave SDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_entry_t data;
+ const ec_slave_t *slave;
+ const ec_sdo_t *sdo;
+ const ec_sdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sdo_spec <= 0) {
+ if (!(sdo = ec_slave_get_sdo_by_pos_const(
+ slave, -data.sdo_spec))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", -data.sdo_spec);
+ return -EINVAL;
+ }
+ } else {
+ if (!(sdo = ec_slave_get_sdo_const(
+ slave, data.sdo_spec))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO 0x%04X does not exist!\n",
+ data.sdo_spec);
+ return -EINVAL;
+ }
+ }
+
+ if (!(entry = ec_sdo_get_entry_const(
+ sdo, data.sdo_entry_subindex))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO entry 0x%04X:%02X does not exist!\n",
+ sdo->index, data.sdo_entry_subindex);
+ return -EINVAL;
+ }
+
+ data.data_type = entry->data_type;
+ data.bit_length = entry->bit_length;
+ data.read_access[EC_SDO_ENTRY_ACCESS_PREOP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP];
+ data.read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
+ data.read_access[EC_SDO_ENTRY_ACCESS_OP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_OP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_PREOP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_OP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_OP];
+ ec_ioctl_strcpy(data.description, entry->description);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Upload SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_upload(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_upload_t data;
+ uint8_t *target;
+ int ret;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!(target = kmalloc(data.target_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes"
+ " for SDO upload.\n", data.target_size);
+ return -ENOMEM;
+ }
+
+ ret = ecrt_master_sdo_upload(master, data.slave_position,
+ data.sdo_index, data.sdo_entry_subindex, target,
+ data.target_size, &data.data_size, &data.abort_code);
+
+ if (!ret) {
+ if (copy_to_user((void __user *) data.target,
+ target, data.data_size)) {
+ kfree(target);
+ return -EFAULT;
+ }
+ }
+
+ kfree(target);
+
+ if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Download SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_download(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_download_t data;
+ uint8_t *sdo_data;
+ int retval;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!(sdo_data = kmalloc(data.data_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes"
+ " for SDO download.\n", data.data_size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) {
+ kfree(sdo_data);
+ return -EFAULT;
+ }
+
+ if (data.complete_access) {
+ retval = ecrt_master_sdo_download_complete(master, data.slave_position,
+ data.sdo_index, sdo_data, data.data_size, &data.abort_code);
+ } else {
+ retval = ecrt_master_sdo_download(master, data.slave_position,
+ data.sdo_index, data.sdo_entry_subindex, sdo_data,
+ data.data_size, &data.abort_code);
+ }
+
+ kfree(sdo_data);
+
+ if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+ retval = -EFAULT;
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Read a slave's SII.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sii_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sii_t data;
+ const ec_slave_t *slave;
+ int retval;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (!data.nwords
+ || data.offset + data.nwords > slave->sii_nwords) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Invalid SII read offset/size %u/%u for slave SII"
+ " size %zu!\n", data.offset, data.nwords, slave->sii_nwords);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void __user *) data.words,
+ slave->sii_words + data.offset, data.nwords * 2))
+ retval = -EFAULT;
+ else
+ retval = 0;
+
+ up(&master->master_sem);
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's SII.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sii_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sii_t data;
+ ec_slave_t *slave;
+ unsigned int byte_size;
+ uint16_t *words;
+ ec_sii_write_request_t request;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!data.nwords) {
+ return 0;
+ }
+
+ byte_size = sizeof(uint16_t) * data.nwords;
+ if (!(words = kmalloc(byte_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %u bytes"
+ " for SII contents.\n", byte_size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(words,
+ (void __user *) data.words, byte_size)) {
+ kfree(words);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(words);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ kfree(words);
+ return -EINVAL;
+ }
+
+ // init SII write request
+ INIT_LIST_HEAD(&request.list);
+ request.slave = slave;
+ request.words = words;
+ request.offset = data.offset;
+ request.nwords = data.nwords;
+ request.state = EC_INT_REQUEST_QUEUED;
+
+ // schedule SII write request.
+ list_add_tail(&request.list, &master->sii_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ kfree(words);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ kfree(words);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Read a slave's registers.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_reg_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_reg_t io;
+ ec_slave_t *slave;
+ ec_reg_request_t request;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (!io.size) {
+ return 0;
+ }
+
+ // init register request
+ ret = ec_reg_request_init(&request, io.size);
+ if (ret) {
+ return ret;
+ }
+
+ ecrt_reg_request_read(&request, io.address, io.size);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->reg_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ if (request.state == EC_INT_REQUEST_SUCCESS) {
+ if (copy_to_user((void __user *) io.data, request.data, io.size)) {
+ return -EFAULT;
+ }
+ }
+ ec_reg_request_clear(&request);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's registers.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_reg_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_reg_t io;
+ ec_slave_t *slave;
+ ec_reg_request_t request;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (!io.size) {
+ return 0;
+ }
+
+ // init register request
+ ret = ec_reg_request_init(&request, io.size);
+ if (ret) {
+ return ret;
+ }
+
+ if (copy_from_user(request.data, (void __user *) io.data, io.size)) {
+ ec_reg_request_clear(&request);
+ return -EFAULT;
+ }
+
+ ecrt_reg_request_write(&request, io.address, io.size);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (io.emergency) {
+ request.ring_position = io.slave_position;
+ // schedule request.
+ list_add_tail(&request.list, &master->emerg_reg_requests);
+ }
+ else {
+ if (!(slave = ec_master_find_slave(master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->reg_requests);
+ }
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ ec_reg_request_clear(&request);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_t data;
+ const ec_slave_config_t *sc;
+ uint8_t i;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ data.alias = sc->alias;
+ data.position = sc->position;
+ data.vendor_id = sc->vendor_id;
+ data.product_code = sc->product_code;
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) {
+ data.syncs[i].dir = sc->sync_configs[i].dir;
+ data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode;
+ data.syncs[i].pdo_count =
+ ec_pdo_list_count(&sc->sync_configs[i].pdos);
+ }
+ data.watchdog_divider = sc->watchdog_divider;
+ data.watchdog_intervals = sc->watchdog_intervals;
+ data.sdo_count = ec_slave_config_sdo_count(sc);
+ data.idn_count = ec_slave_config_idn_count(sc);
+ data.slave_position = sc->slave ? sc->slave->ring_position : -1;
+ data.dc_assign_activate = sc->dc_assign_activate;
+ for (i = 0; i < EC_SYNC_SIGNAL_COUNT; i++) {
+ data.dc_sync[i] = sc->dc_sync[i];
+ }
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration PDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ const ec_slave_config_t *sc;
+ const ec_pdo_t *pdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (data.sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_MASTER_ERR(master, "Invalid sync manager index %u!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sc->sync_configs[data.sync_index].pdos,
+ data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid PDO position!\n");
+ return -EINVAL;
+ }
+
+ data.index = pdo->index;
+ data.entry_count = ec_pdo_entry_count(pdo);
+ ec_ioctl_strcpy(data.name, pdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration PDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_pdo_entry_t data;
+ const ec_slave_config_t *sc;
+ const ec_pdo_t *pdo;
+ const ec_pdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (data.sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_MASTER_ERR(master, "Invalid sync manager index %u!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sc->sync_configs[data.sync_index].pdos,
+ data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid PDO position!\n");
+ return -EINVAL;
+ }
+
+ if (!(entry = ec_pdo_find_entry_by_pos_const(
+ pdo, data.entry_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Entry not found!\n");
+ return -EINVAL;
+ }
+
+ data.index = entry->index;
+ data.subindex = entry->subindex;
+ data.bit_length = entry->bit_length;
+ ec_ioctl_strcpy(data.name, entry->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration SDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_sdo_t *ioctl;
+ const ec_slave_config_t *sc;
+ const ec_sdo_request_t *req;
+
+ if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(ioctl);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config_const(
+ master, ioctl->config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ ioctl->config_index);
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ if (!(req = ec_slave_config_get_sdo_by_pos_const(
+ sc, ioctl->sdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid SDO position!\n");
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ ioctl->index = req->index;
+ ioctl->subindex = req->subindex;
+ ioctl->size = req->data_size;
+ memcpy(ioctl->data, req->data,
+ min((u32) ioctl->size, (u32) EC_MAX_SDO_DATA_SIZE));
+ ioctl->complete_access = req->complete_access;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ kfree(ioctl);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration IDN information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_idn_t *ioctl;
+ const ec_slave_config_t *sc;
+ const ec_soe_request_t *req;
+
+ if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(ioctl);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config_const(
+ master, ioctl->config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ ioctl->config_index);
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ if (!(req = ec_slave_config_get_idn_by_pos_const(
+ sc, ioctl->idn_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid IDN position!\n");
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ ioctl->drive_no = req->drive_no;
+ ioctl->idn = req->idn;
+ ioctl->state = req->state;
+ ioctl->size = req->data_size;
+ memcpy(ioctl->data, req->data,
+ min((u32) ioctl->size, (u32) EC_MAX_IDN_DATA_SIZE));
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ kfree(ioctl);
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+
+/** Get EoE handler information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_eoe_handler(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_eoe_handler_t data;
+ const ec_eoe_t *eoe;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(eoe = ec_master_get_eoe_handler_const(master, data.eoe_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "EoE handler %u does not exist!\n",
+ data.eoe_index);
+ return -EINVAL;
+ }
+
+ if (eoe->slave) {
+ data.slave_position = eoe->slave->ring_position;
+ } else {
+ data.slave_position = 0xffff;
+ }
+ snprintf(data.name, EC_DATAGRAM_NAME_SIZE, eoe->dev->name);
+ data.open = eoe->opened;
+ data.rx_bytes = eoe->stats.tx_bytes;
+ data.rx_rate = eoe->tx_rate;
+ data.tx_bytes = eoe->stats.rx_bytes;
+ data.tx_rate = eoe->tx_rate;
+ data.tx_queued_frames = eoe->tx_queued_frames;
+ data.tx_queue_size = eoe->tx_queue_size;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+#endif
+
+/*****************************************************************************/
+
+/** Request the master from userspace.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_master_t *m;
+ int ret = 0;
+
+ m = ecrt_request_master_err(master->index);
+ if (IS_ERR(m)) {
+ ret = PTR_ERR(m);
+ } else {
+ ctx->requested = 1;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Create a domain.
+ *
+ * \return Domain index on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_create_domain(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ domain = ecrt_master_create_domain_err(master);
+ if (IS_ERR(domain))
+ return PTR_ERR(domain);
+
+ return domain->index;
+}
+
+/*****************************************************************************/
+
+/** Create a slave configuration.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_create_slave_config(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc, *entry;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ sc = ecrt_master_slave_config_err(master, data.alias, data.position,
+ data.vendor_id, data.product_code);
+ if (IS_ERR(sc))
+ return PTR_ERR(sc);
+
+ data.config_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ list_for_each_entry(entry, &master->configs, list) {
+ if (entry == sc)
+ break;
+ data.config_index++;
+ }
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Select the DC reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_select_ref_clock(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ unsigned long config_index = (unsigned long) arg;
+ ec_slave_config_t *sc = NULL;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (config_index != 0xFFFFFFFF) {
+ if (!(sc = ec_master_get_config(master, config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+ }
+
+ ecrt_master_select_reference_clock(master, sc);
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Activates the master.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_activate(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_master_activate_t io;
+ ec_domain_t *domain;
+ off_t offset;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ io.process_data = NULL;
+
+ /* Get the sum of the domains' process data sizes. */
+
+ ctx->process_data_size = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ list_for_each_entry(domain, &master->domains, list) {
+ ctx->process_data_size += ecrt_domain_size(domain);
+ }
+
+ up(&master->master_sem);
+
+ if (ctx->process_data_size) {
+ ctx->process_data = vmalloc(ctx->process_data_size);
+ if (!ctx->process_data) {
+ ctx->process_data_size = 0;
+ return -ENOMEM;
+ }
+
+ /* Set the memory as external process data memory for the
+ * domains.
+ */
+ offset = 0;
+ list_for_each_entry(domain, &master->domains, list) {
+ ecrt_domain_external_memory(domain,
+ ctx->process_data + offset);
+ offset += ecrt_domain_size(domain);
+ }
+
+#ifdef EC_IOCTL_RTDM
+ /* RTDM uses a different approach for memory-mapping, which has to be
+ * initiated by the kernel.
+ */
+ ret = ec_rtdm_mmap(ctx, &io.process_data);
+ if (ret < 0) {
+ EC_MASTER_ERR(master, "Failed to map process data"
+ " memory to user space (code %i).\n", ret);
+ return ret;
+ }
+#endif
+ }
+
+ io.process_data_size = ctx->process_data_size;
+
+#ifndef EC_IOCTL_RTDM
+ ecrt_master_callbacks(master, ec_master_internal_send_cb,
+ ec_master_internal_receive_cb, master);
+#endif
+
+ ret = ecrt_master_activate(master);
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user((void __user *) arg, &io,
+ sizeof(ec_ioctl_master_activate_t)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Deactivates the master.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_deactivate(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ ecrt_master_deactivate(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set max. number of databytes in a cycle
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_set_send_interval(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ size_t send_interval;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&send_interval, (void __user *) arg,
+ sizeof(send_interval))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ ec_master_set_send_interval(master, send_interval);
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Send frames.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_send(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_send(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Receive frames.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_receive(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_receive(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the master state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_master_state_t data;
+
+ ecrt_master_state(master, &data);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the link state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_link_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_link_state_t ioctl;
+ ec_master_link_state_t state;
+ int ret;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ ret = ecrt_master_link_state(master, ioctl.dev_idx, &state);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) ioctl.state, &state, sizeof(state))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set the master DC application time.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_app_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint64_t time;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&time, (void __user *) arg, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ ecrt_master_application_time(master, time);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_ref(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_reference_clock(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_ref_to(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint64_t time;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&time, (void __user *) arg, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ ecrt_master_sync_reference_clock_to(master, time);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the slave clocks.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_slaves(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_slave_clocks(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the system time of the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_ref_clock_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint32_t time;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ret = ecrt_master_reference_clock_time(master, &time);
+ if (ret) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) arg, &time, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the sync monitoring datagram.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_mon_queue(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_monitor_queue(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Processes the sync monitoring datagram.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_mon_process(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint32_t time_diff;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ time_diff = ecrt_master_sync_monitor_process(master);
+
+ if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Reset configuration.
+ *
+ * \return Always zero (success).
+ */
+static ATTRIBUTES int ec_ioctl_reset(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ down(&master->master_sem);
+ ecrt_master_reset(master);
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configure a sync manager.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_sync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+ unsigned int i;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ ret = -EFAULT;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) {
+ if (data.syncs[i].config_this) {
+ ret = ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir,
+ data.syncs[i].watchdog_mode);
+ if (ret) {
+ goto out_up;
+ }
+ }
+ }
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Configure a slave's watchdogs.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_watchdog(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ ret = -EFAULT;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+
+ ecrt_slave_config_watchdog(sc,
+ data.watchdog_divider, data.watchdog_intervals);
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Add a PDO to the assignment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_add_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index);
+}
+
+/*****************************************************************************/
+
+/** Clears the PDO assignment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_clear_pdos(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ecrt_slave_config_pdo_assign_clear(sc, data.sync_index);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Add an entry to a PDO's mapping.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_add_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_add_pdo_entry_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ return ecrt_slave_config_pdo_mapping_add(sc, data.pdo_index,
+ data.entry_index, data.entry_subindex, data.entry_bit_length);
+}
+
+/*****************************************************************************/
+
+/** Clears the mapping of a PDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_clear_entries(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ecrt_slave_config_pdo_mapping_clear(sc, data.index);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Registers a PDO entry.
+ *
+ * \return Process data offset on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_reg_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_pdo_entry_t data;
+ ec_slave_config_t *sc;
+ ec_domain_t *domain;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ if (!(domain = ec_master_find_domain(master, data.domain_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc or domain could be invalidated */
+
+ ret = ecrt_slave_config_reg_pdo_entry(sc, data.entry_index,
+ data.entry_subindex, domain, &data.bit_position);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Registers a PDO entry by its position.
+ *
+ * \return Process data offset on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_reg_pdo_pos(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_pdo_pos_t io;
+ ec_slave_config_t *sc;
+ ec_domain_t *domain;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ if (!(domain = ec_master_find_domain(master, io.domain_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc or domain could be invalidated */
+
+ ret = ecrt_slave_config_reg_pdo_entry_pos(sc, io.sync_index,
+ io.pdo_pos, io.entry_pos, domain, &io.bit_position);
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Sets the DC AssignActivate word and the sync signal times.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_dc(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ ecrt_slave_config_dc(sc, data.dc_assign_activate,
+ data.dc_sync[0].cycle_time,
+ data.dc_sync[0].shift_time,
+ data.dc_sync[1].cycle_time,
+ data.dc_sync[1].shift_time);
+
+ up(&master->master_sem);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configures an SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_sdo_t data;
+ ec_slave_config_t *sc;
+ uint8_t *sdo_data = NULL;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (!data.size)
+ return -EINVAL;
+
+ if (!(sdo_data = kmalloc(data.size, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(sdo_data, (void __user *) data.data, data.size)) {
+ kfree(sdo_data);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(sdo_data);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ kfree(sdo_data);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ if (data.complete_access) {
+ ret = ecrt_slave_config_complete_sdo(sc,
+ data.index, sdo_data, data.size);
+ } else {
+ ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data,
+ data.size);
+ }
+ kfree(sdo_data);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Set the emergency ring buffer size.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_size(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_size(sc, io.size);
+
+ up(&master->master_sem);
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Get an emergency message from the ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_pop(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ u8 msg[EC_COE_EMERGENCY_MSG_SIZE];
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_pop(sc, msg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) io.target, msg, sizeof(msg))) {
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Clear the emergency ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_clear(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ return ecrt_slave_config_emerg_clear(sc);
+}
+
+/*****************************************************************************/
+
+/** Get the number of emergency overruns.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_overruns(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_overruns(sc);
+ if (ret < 0) {
+ return ret;
+ }
+
+ io.overruns = ret;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create an SDO request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_sdo_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ data.request_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ sc = ec_master_get_config(master, data.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(req, &sc->sdo_requests, list) {
+ data.request_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ req = ecrt_slave_config_create_sdo_request_err(sc, data.sdo_index,
+ data.sdo_subindex, data.size);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create a register request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_reg_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ io.request_index = 0;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ sc = ec_master_get_config(master, io.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(reg, &sc->reg_requests, list) {
+ io.request_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ reg = ecrt_slave_config_create_reg_request_err(sc, io.mem_size);
+ if (IS_ERR(reg)) {
+ return PTR_ERR(reg);
+ }
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create a VoE handler.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_voe_handler(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ data.voe_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ sc = ec_master_get_config(master, data.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(voe, &sc->voe_handlers, list) {
+ data.voe_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ voe = ecrt_slave_config_create_voe_handler_err(sc, data.size);
+ if (IS_ERR(voe))
+ return PTR_ERR(voe);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the slave configuration's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_state_t data;
+ const ec_slave_config_t *sc;
+ ec_slave_config_state_t state;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because sc will not be deleted in the
+ * meantime. */
+
+ if (!(sc = ec_master_get_config_const(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_slave_config_state(sc, &state);
+
+ if (copy_to_user((void __user *) data.state, &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configures an IDN.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_idn_t ioctl;
+ ec_slave_config_t *sc;
+ uint8_t *data = NULL;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl)))
+ return -EFAULT;
+
+ if (!ioctl.size)
+ return -EINVAL;
+
+ if (!(data = kmalloc(ioctl.size, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(data, (void __user *) ioctl.data, ioctl.size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(data);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, ioctl.config_index))) {
+ up(&master->master_sem);
+ kfree(data);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ret = ecrt_slave_config_idn(
+ sc, ioctl.drive_no, ioctl.idn, ioctl.al_state, data, ioctl.size);
+ kfree(data);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Gets the domain's data size.
+ *
+ * \return Domain size, or a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_size(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ const ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ list_for_each_entry(domain, &master->domains, list) {
+ if (domain->index == (unsigned long) arg) {
+ size_t size = ecrt_domain_size(domain);
+ up(&master->master_sem);
+ return size;
+ }
+ }
+
+ up(&master->master_sem);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+/** Gets the domain's offset in the total process data.
+ *
+ * \return Domain offset, or a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_offset(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ int offset = 0;
+ const ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ list_for_each_entry(domain, &master->domains, list) {
+ if (domain->index == (unsigned long) arg) {
+ up(&master->master_sem);
+ return offset;
+ }
+ offset += ecrt_domain_size(domain);
+ }
+
+ up(&master->master_sem);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+/** Process the domain.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_process(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain(master, (unsigned long) arg))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_process(domain);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the domain.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_queue(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain(master, (unsigned long) arg))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_queue(domain);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the domain state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_domain_state_t data;
+ const ec_domain_t *domain;
+ ec_domain_state_t state;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_state(domain, &state);
+
+ if (copy_to_user((void __user *) data.state, &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets an SDO request's SDO index and subindex.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_index(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_index(req, data.sdo_index, data.sdo_subindex);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets an SDO request's timeout.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_timeout(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_timeout(req, data.timeout);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an SDO request's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ data.state = ecrt_sdo_request_state(req);
+ if (data.state == EC_REQUEST_SUCCESS && req->dir == EC_DIR_INPUT)
+ data.size = ecrt_sdo_request_data_size(req);
+ else
+ data.size = 0;
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SDO read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_read(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SDO write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (!data.size) {
+ EC_MASTER_ERR(master, "SDO download: Data size may not be zero!\n");
+ return -EINVAL;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ret = ec_sdo_request_alloc(req, data.size);
+ if (ret)
+ return ret;
+
+ if (copy_from_user(req->data, (void __user *) data.data, data.size))
+ return -EFAULT;
+
+ req->data_size = data.size;
+ ecrt_sdo_request_write(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read SDO data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) data.data, ecrt_sdo_request_data(req),
+ ecrt_sdo_request_data_size(req)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read register data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (io.mem_size <= 0) {
+ return 0;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) io.data, ecrt_reg_request_data(reg),
+ min(reg->mem_size, io.mem_size))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an register request's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ io.state = ecrt_reg_request_state(reg);
+ io.new_data = io.state == EC_REQUEST_SUCCESS && reg->dir == EC_DIR_INPUT;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (io.transfer_size > reg->mem_size) {
+ return -EOVERFLOW;
+ }
+
+ if (copy_from_user(reg->data, (void __user *) io.data,
+ io.transfer_size)) {
+ return -EFAULT;
+ }
+
+ ecrt_reg_request_write(reg, io.address, io.transfer_size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (io.transfer_size > reg->mem_size) {
+ return -EOVERFLOW;
+ }
+
+ ecrt_reg_request_read(reg, io.address, io.transfer_size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets the VoE send header.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_send_header(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+ uint32_t vendor_id;
+ uint16_t vendor_type;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (get_user(vendor_id, data.vendor_id))
+ return -EFAULT;
+
+ if (get_user(vendor_type, data.vendor_type))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_send_header(voe, vendor_id, vendor_type);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets the received VoE header.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_rec_header(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+ uint32_t vendor_id;
+ uint16_t vendor_type;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_received_header(voe, &vendor_id, &vendor_type);
+
+ if (likely(data.vendor_id))
+ if (put_user(vendor_id, data.vendor_id))
+ return -EFAULT;
+
+ if (likely(data.vendor_type))
+ if (put_user(vendor_type, data.vendor_type))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_read(voe);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE read operation without sending a sync message first.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_read_nosync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_read_nosync(voe);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ if (data.size) {
+ if (data.size > ec_voe_handler_mem_size(voe))
+ return -EOVERFLOW;
+
+ if (copy_from_user(ecrt_voe_handler_data(voe),
+ (void __user *) data.data, data.size))
+ return -EFAULT;
+ }
+
+ ecrt_voe_handler_write(voe, data.size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Executes the VoE state machine.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_exec(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ data.state = ecrt_voe_handler_execute(voe);
+ if (data.state == EC_REQUEST_SUCCESS && voe->dir == EC_DIR_INPUT)
+ data.size = ecrt_voe_handler_data_size(voe);
+ else
+ data.size = 0;
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Reads the received VoE data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) data.data, ecrt_voe_handler_data(voe),
+ ecrt_voe_handler_data_size(voe)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read a file from a slave via FoE.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_foe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_foe_t io;
+ ec_foe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ ec_foe_request_init(&request, io.file_name);
+ ret = ec_foe_request_alloc(&request, 10000); // FIXME
+ if (ret) {
+ ec_foe_request_clear(&request);
+ return ret;
+ }
+
+ ec_foe_request_read(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling FoE read request.\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->foe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ io.result = request.result;
+ io.error_code = request.error_code;
+
+ if (request.state != EC_INT_REQUEST_SUCCESS) {
+ io.data_size = 0;
+ ret = -EIO;
+ } else {
+ if (request.data_size > io.buffer_size) {
+ EC_SLAVE_ERR(slave, "%s(): Buffer too small.\n", __func__);
+ ec_foe_request_clear(&request);
+ return -EOVERFLOW;
+ }
+ io.data_size = request.data_size;
+ if (copy_to_user((void __user *) io.buffer,
+ request.buffer, io.data_size)) {
+ ec_foe_request_clear(&request);
+ return -EFAULT;
+ }
+ ret = 0;
+ }
+
+ if (__copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ ret = -EFAULT;
+ }
+
+ ec_foe_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Write a file to a slave via FoE
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_foe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_foe_t io;
+ ec_foe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ ec_foe_request_init(&request, io.file_name);
+
+ ret = ec_foe_request_alloc(&request, io.buffer_size);
+ if (ret) {
+ ec_foe_request_clear(&request);
+ return ret;
+ }
+
+ if (copy_from_user(request.buffer,
+ (void __user *) io.buffer, io.buffer_size)) {
+ ec_foe_request_clear(&request);
+ return -EFAULT;
+ }
+
+ request.data_size = io.buffer_size;
+ ec_foe_request_write(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_foe_request_clear(&request);
+ 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);
+ ec_foe_request_clear(&request);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling FoE write request.\n");
+
+ // schedule FoE write request.
+ list_add_tail(&request.list, &slave->foe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ io.result = request.result;
+ io.error_code = request.error_code;
+
+ ret = request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+
+ if (__copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ ret = -EFAULT;
+ }
+
+ ec_foe_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Read an SoE IDN.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_soe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_soe_read_t ioctl;
+ u8 *data;
+ int retval;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ data = kmalloc(ioctl.mem_size, GFP_KERNEL);
+ if (!data) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n",
+ ioctl.mem_size);
+ return -ENOMEM;
+ }
+
+ retval = ecrt_master_read_idn(master, ioctl.slave_position,
+ ioctl.drive_no, ioctl.idn, data, ioctl.mem_size, &ioctl.data_size,
+ &ioctl.error_code);
+ if (retval) {
+ kfree(data);
+ return retval;
+ }
+
+ if (copy_to_user((void __user *) ioctl.data,
+ data, ioctl.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ kfree(data);
+
+ if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
+ retval = -EFAULT;
+ }
+
+ EC_MASTER_DBG(master, 1, "Finished SoE read request.\n");
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Write an IDN to a slave via SoE.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_soe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_soe_write_t ioctl;
+ u8 *data;
+ int retval;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ data = kmalloc(ioctl.data_size, GFP_KERNEL);
+ if (!data) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n",
+ ioctl.data_size);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, (void __user *) ioctl.data, ioctl.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ retval = ecrt_master_write_idn(master, ioctl.slave_position,
+ ioctl.drive_no, ioctl.idn, data, ioctl.data_size,
+ &ioctl.error_code);
+ kfree(data);
+ if (retval) {
+ return retval;
+ }
+
+ if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
+ retval = -EFAULT;
+ }
+
+ EC_MASTER_DBG(master, 1, "Finished SoE write request.\n");
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** ioctl() function to use.
+ */
+#ifdef EC_IOCTL_RTDM
+#define EC_IOCTL ec_ioctl_rtdm
+#else
+#define EC_IOCTL ec_ioctl
+#endif
+
+/** Called when an ioctl() command is issued.
+ *
+ * \return ioctl() return code.
+ */
+long EC_IOCTL(
+ ec_master_t *master, /**< EtherCAT master. */
+ ec_ioctl_context_t *ctx, /**< Device context. */
+ unsigned int cmd, /**< ioctl() command identifier. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+#if DEBUG_LATENCY
+ cycles_t a = get_cycles(), b;
+ unsigned int t;
+#endif
+ int ret;
+
+ switch (cmd) {
+ case EC_IOCTL_MODULE:
+ ret = ec_ioctl_module(arg);
+ break;
+ case EC_IOCTL_MASTER:
+ ret = ec_ioctl_master(master, arg);
+ break;
+ case EC_IOCTL_SLAVE:
+ ret = ec_ioctl_slave(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC:
+ ret = ec_ioctl_slave_sync(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC_PDO:
+ ret = ec_ioctl_slave_sync_pdo(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC_PDO_ENTRY:
+ ret = ec_ioctl_slave_sync_pdo_entry(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN:
+ ret = ec_ioctl_domain(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN_FMMU:
+ ret = ec_ioctl_domain_fmmu(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN_DATA:
+ ret = ec_ioctl_domain_data(master, arg);
+ break;
+ case EC_IOCTL_MASTER_DEBUG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_master_debug(master, arg);
+ break;
+ case EC_IOCTL_MASTER_RESCAN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_master_rescan(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_STATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_state(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO:
+ ret = ec_ioctl_slave_sdo(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_ENTRY:
+ ret = ec_ioctl_slave_sdo_entry(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_UPLOAD:
+ ret = ec_ioctl_slave_sdo_upload(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_DOWNLOAD:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_sdo_download(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SII_READ:
+ ret = ec_ioctl_slave_sii_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SII_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_sii_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_REG_READ:
+ ret = ec_ioctl_slave_reg_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_REG_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_reg_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_FOE_READ:
+ ret = ec_ioctl_slave_foe_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_FOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_foe_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SOE_READ:
+ ret = ec_ioctl_slave_soe_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_soe_write(master, arg);
+ break;
+ case EC_IOCTL_CONFIG:
+ ret = ec_ioctl_config(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_PDO:
+ ret = ec_ioctl_config_pdo(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_PDO_ENTRY:
+ ret = ec_ioctl_config_pdo_entry(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_SDO:
+ ret = ec_ioctl_config_sdo(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_IDN:
+ ret = ec_ioctl_config_idn(master, arg);
+ break;
+#ifdef EC_EOE
+ case EC_IOCTL_EOE_HANDLER:
+ ret = ec_ioctl_eoe_handler(master, arg);
+ break;
+#endif
+ case EC_IOCTL_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_CREATE_DOMAIN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_create_domain(master, arg, ctx);
+ break;
+ case EC_IOCTL_CREATE_SLAVE_CONFIG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_create_slave_config(master, arg, ctx);
+ break;
+ case EC_IOCTL_SELECT_REF_CLOCK:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_select_ref_clock(master, arg, ctx);
+ break;
+ case EC_IOCTL_ACTIVATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_activate(master, arg, ctx);
+ break;
+ case EC_IOCTL_DEACTIVATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_deactivate(master, arg, ctx);
+ break;
+ case EC_IOCTL_SEND:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_send(master, arg, ctx);
+ break;
+ case EC_IOCTL_RECEIVE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_receive(master, arg, ctx);
+ break;
+ case EC_IOCTL_MASTER_STATE:
+ ret = ec_ioctl_master_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_MASTER_LINK_STATE:
+ ret = ec_ioctl_master_link_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_APP_TIME:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_app_time(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_REF:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_ref(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_REF_TO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_ref_to(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_SLAVES:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_slaves(master, arg, ctx);
+ break;
+ case EC_IOCTL_REF_CLOCK_TIME:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_ref_clock_time(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_MON_QUEUE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_mon_queue(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_MON_PROCESS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_mon_process(master, arg, ctx);
+ break;
+ case EC_IOCTL_RESET:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reset(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SYNC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_sync(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_WATCHDOG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_watchdog(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_ADD_PDO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_add_pdo(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_CLEAR_PDOS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_clear_pdos(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_ADD_ENTRY:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_add_entry(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_CLEAR_ENTRIES:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_clear_entries(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_PDO_ENTRY:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_reg_pdo_entry(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_PDO_POS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_reg_pdo_pos(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_DC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_dc(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SDO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_sdo(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_SIZE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_size(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_POP:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_pop(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_CLEAR:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_clear(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_OVERRUNS:
+ ret = ec_ioctl_sc_emerg_overruns(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SDO_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_sdo_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_reg_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_VOE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_voe_handler(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_STATE:
+ ret = ec_ioctl_sc_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_IDN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_idn(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_SIZE:
+ ret = ec_ioctl_domain_size(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_OFFSET:
+ ret = ec_ioctl_domain_offset(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_PROCESS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_domain_process(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_QUEUE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_domain_queue(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_STATE:
+ ret = ec_ioctl_domain_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_INDEX:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_index(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_TIMEOUT:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_timeout(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_STATE:
+ ret = ec_ioctl_sdo_request_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_DATA:
+ ret = ec_ioctl_sdo_request_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_DATA:
+ ret = ec_ioctl_reg_request_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_STATE:
+ ret = ec_ioctl_reg_request_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reg_request_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reg_request_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_SEND_HEADER:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_send_header(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_REC_HEADER:
+ ret = ec_ioctl_voe_rec_header(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_READ_NOSYNC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_read_nosync(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_EXEC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_exec(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_DATA:
+ ret = ec_ioctl_voe_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_SET_SEND_INTERVAL:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_set_send_interval(master, arg, ctx);
+ break;
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+#if DEBUG_LATENCY
+ b = get_cycles();
+ t = (unsigned int) ((b - a) * 1000LL) / cpu_khz;
+ if (t > 50) {
+ EC_MASTER_WARN(master, "ioctl(0x%02x) took %u us.\n",
+ _IOC_NR(cmd), t);
+ }
+#endif
+
+ return ret;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/ioctl.h b/net/ethercat/master/ioctl.h
new file mode 100644
index 000000000000..a1d597b72bdf
--- /dev/null
+++ b/net/ethercat/master/ioctl.h
@@ -0,0 +1,782 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master character device IOCTL commands.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_IOCTL_H__
+#define __EC_IOCTL_H__
+
+#include <linux/ioctl.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** \cond */
+
+#define EC_IOCTL_TYPE 0xa4
+
+#define EC_IO(nr) _IO(EC_IOCTL_TYPE, nr)
+#define EC_IOR(nr, type) _IOR(EC_IOCTL_TYPE, nr, type)
+#define EC_IOW(nr, type) _IOW(EC_IOCTL_TYPE, nr, type)
+#define EC_IOWR(nr, type) _IOWR(EC_IOCTL_TYPE, nr, type)
+
+/** EtherCAT master ioctl() version magic.
+ *
+ * Increment this when changing the ioctl interface!
+ */
+#define EC_IOCTL_VERSION_MAGIC 30
+
+// Command-line tool
+#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)
+#define EC_IOCTL_MASTER EC_IOR(0x01, ec_ioctl_master_t)
+#define EC_IOCTL_SLAVE EC_IOWR(0x02, ec_ioctl_slave_t)
+#define EC_IOCTL_SLAVE_SYNC EC_IOWR(0x03, ec_ioctl_slave_sync_t)
+#define EC_IOCTL_SLAVE_SYNC_PDO EC_IOWR(0x04, ec_ioctl_slave_sync_pdo_t)
+#define EC_IOCTL_SLAVE_SYNC_PDO_ENTRY EC_IOWR(0x05, ec_ioctl_slave_sync_pdo_entry_t)
+#define EC_IOCTL_DOMAIN EC_IOWR(0x06, ec_ioctl_domain_t)
+#define EC_IOCTL_DOMAIN_FMMU EC_IOWR(0x07, ec_ioctl_domain_fmmu_t)
+#define EC_IOCTL_DOMAIN_DATA EC_IOWR(0x08, ec_ioctl_domain_data_t)
+#define EC_IOCTL_MASTER_DEBUG EC_IO(0x09)
+#define EC_IOCTL_MASTER_RESCAN EC_IO(0x0a)
+#define EC_IOCTL_SLAVE_STATE EC_IOW(0x0b, ec_ioctl_slave_state_t)
+#define EC_IOCTL_SLAVE_SDO EC_IOWR(0x0c, ec_ioctl_slave_sdo_t)
+#define EC_IOCTL_SLAVE_SDO_ENTRY EC_IOWR(0x0d, ec_ioctl_slave_sdo_entry_t)
+#define EC_IOCTL_SLAVE_SDO_UPLOAD EC_IOWR(0x0e, ec_ioctl_slave_sdo_upload_t)
+#define EC_IOCTL_SLAVE_SDO_DOWNLOAD EC_IOWR(0x0f, ec_ioctl_slave_sdo_download_t)
+#define EC_IOCTL_SLAVE_SII_READ EC_IOWR(0x10, ec_ioctl_slave_sii_t)
+#define EC_IOCTL_SLAVE_SII_WRITE EC_IOW(0x11, ec_ioctl_slave_sii_t)
+#define EC_IOCTL_SLAVE_REG_READ EC_IOWR(0x12, ec_ioctl_slave_reg_t)
+#define EC_IOCTL_SLAVE_REG_WRITE EC_IOW(0x13, ec_ioctl_slave_reg_t)
+#define EC_IOCTL_SLAVE_FOE_READ EC_IOWR(0x14, ec_ioctl_slave_foe_t)
+#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)
+#ifdef EC_EOE
+#define EC_IOCTL_EOE_HANDLER EC_IOWR(0x1d, ec_ioctl_eoe_handler_t)
+#endif
+
+// Application interface
+#define EC_IOCTL_REQUEST EC_IO(0x1e)
+#define EC_IOCTL_CREATE_DOMAIN EC_IO(0x1f)
+#define EC_IOCTL_CREATE_SLAVE_CONFIG EC_IOWR(0x20, ec_ioctl_config_t)
+#define EC_IOCTL_SELECT_REF_CLOCK EC_IOW(0x21, uint32_t)
+#define EC_IOCTL_ACTIVATE EC_IOR(0x22, ec_ioctl_master_activate_t)
+#define EC_IOCTL_DEACTIVATE EC_IO(0x23)
+#define EC_IOCTL_SEND EC_IO(0x24)
+#define EC_IOCTL_RECEIVE EC_IO(0x25)
+#define EC_IOCTL_MASTER_STATE EC_IOR(0x26, ec_master_state_t)
+#define EC_IOCTL_MASTER_LINK_STATE EC_IOWR(0x27, ec_ioctl_link_state_t)
+#define EC_IOCTL_APP_TIME EC_IOW(0x28, uint64_t)
+#define EC_IOCTL_SYNC_REF EC_IO(0x29)
+#define EC_IOCTL_SYNC_REF_TO EC_IOW(0x2a, uint64_t)
+#define EC_IOCTL_SYNC_SLAVES EC_IO(0x2b)
+#define EC_IOCTL_REF_CLOCK_TIME EC_IOR(0x2c, uint32_t)
+#define EC_IOCTL_SYNC_MON_QUEUE EC_IO(0x2d)
+#define EC_IOCTL_SYNC_MON_PROCESS EC_IOR(0x2e, uint32_t)
+#define EC_IOCTL_RESET EC_IO(0x2f)
+#define EC_IOCTL_SC_SYNC EC_IOW(0x30, ec_ioctl_config_t)
+#define EC_IOCTL_SC_WATCHDOG EC_IOW(0x31, ec_ioctl_config_t)
+#define EC_IOCTL_SC_ADD_PDO EC_IOW(0x32, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_CLEAR_PDOS EC_IOW(0x33, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_ADD_ENTRY EC_IOW(0x34, ec_ioctl_add_pdo_entry_t)
+#define EC_IOCTL_SC_CLEAR_ENTRIES EC_IOW(0x35, ec_ioctl_config_pdo_t)
+#define EC_IOCTL_SC_REG_PDO_ENTRY EC_IOWR(0x36, ec_ioctl_reg_pdo_entry_t)
+#define EC_IOCTL_SC_REG_PDO_POS EC_IOWR(0x37, ec_ioctl_reg_pdo_pos_t)
+#define EC_IOCTL_SC_DC EC_IOW(0x38, ec_ioctl_config_t)
+#define EC_IOCTL_SC_SDO EC_IOW(0x39, ec_ioctl_sc_sdo_t)
+#define EC_IOCTL_SC_EMERG_SIZE EC_IOW(0x3a, ec_ioctl_sc_emerg_t)
+#define EC_IOCTL_SC_EMERG_POP EC_IOWR(0x3b, ec_ioctl_sc_emerg_t)
+#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x3c, ec_ioctl_sc_emerg_t)
+#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x3d, ec_ioctl_sc_emerg_t)
+#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x3e, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x3f, ec_ioctl_reg_request_t)
+#define EC_IOCTL_SC_VOE EC_IOWR(0x40, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_STATE EC_IOWR(0x41, ec_ioctl_sc_state_t)
+#define EC_IOCTL_SC_IDN EC_IOW(0x42, ec_ioctl_sc_idn_t)
+#define EC_IOCTL_DOMAIN_SIZE EC_IO(0x43)
+#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x44)
+#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x45)
+#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x46)
+#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x47, ec_ioctl_domain_state_t)
+#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x48, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x49, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x4a, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x4b, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x4c, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x4d, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x4e, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x4f, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x50, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x51, ec_ioctl_reg_request_t)
+#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x52, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x53, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ EC_IOW(0x54, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x55, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_WRITE EC_IOWR(0x56, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_EXEC EC_IOWR(0x57, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_DATA EC_IOWR(0x58, ec_ioctl_voe_t)
+#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x59, size_t)
+
+/*****************************************************************************/
+
+#define EC_IOCTL_STRING_SIZE 64
+
+/*****************************************************************************/
+
+typedef struct {
+ uint32_t ioctl_version_magic;
+ uint32_t master_count;
+} ec_ioctl_module_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ uint32_t slave_count;
+ uint32_t config_count;
+ uint32_t domain_count;
+#ifdef EC_EOE
+ uint32_t eoe_handler_count;
+#endif
+ uint8_t phase;
+ uint8_t active;
+ uint8_t scan_busy;
+ struct ec_ioctl_device {
+ uint8_t address[6];
+ uint8_t attached;
+ uint8_t link_state;
+ uint64_t tx_count;
+ uint64_t rx_count;
+ uint64_t tx_bytes;
+ uint64_t rx_bytes;
+ uint64_t tx_errors;
+ int32_t tx_frame_rates[EC_RATE_COUNT];
+ int32_t rx_frame_rates[EC_RATE_COUNT];
+ int32_t tx_byte_rates[EC_RATE_COUNT];
+ int32_t rx_byte_rates[EC_RATE_COUNT];
+ } devices[EC_MAX_NUM_DEVICES];
+ uint32_t num_devices;
+ uint64_t tx_count;
+ uint64_t rx_count;
+ uint64_t tx_bytes;
+ uint64_t rx_bytes;
+ int32_t tx_frame_rates[EC_RATE_COUNT];
+ int32_t rx_frame_rates[EC_RATE_COUNT];
+ int32_t tx_byte_rates[EC_RATE_COUNT];
+ int32_t rx_byte_rates[EC_RATE_COUNT];
+ int32_t loss_rates[EC_RATE_COUNT];
+ uint64_t app_time;
+ uint64_t dc_ref_time;
+ uint16_t ref_clock;
+} ec_ioctl_master_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // input
+ uint16_t position;
+
+ // outputs
+ unsigned int device_index;
+ uint32_t vendor_id;
+ uint32_t product_code;
+ uint32_t revision_number;
+ uint32_t serial_number;
+ uint16_t alias;
+ uint16_t boot_rx_mailbox_offset;
+ uint16_t boot_rx_mailbox_size;
+ uint16_t boot_tx_mailbox_offset;
+ uint16_t boot_tx_mailbox_size;
+ uint16_t std_rx_mailbox_offset;
+ uint16_t std_rx_mailbox_size;
+ uint16_t std_tx_mailbox_offset;
+ uint16_t std_tx_mailbox_size;
+ uint16_t mailbox_protocols;
+ uint8_t has_general_category;
+ ec_sii_coe_details_t coe_details;
+ ec_sii_general_flags_t general_flags;
+ int16_t current_on_ebus;
+ struct {
+ ec_slave_port_desc_t desc;
+ ec_slave_port_link_t link;
+ uint32_t receive_time;
+ uint16_t next_slave;
+ uint32_t delay_to_next_dc;
+ } ports[EC_MAX_PORTS];
+ uint8_t fmmu_bit;
+ uint8_t dc_supported;
+ ec_slave_dc_range_t dc_range;
+ uint8_t has_dc_system_time;
+ uint32_t transmission_delay;
+ uint8_t al_state;
+ uint8_t error_flag;
+ uint8_t sync_count;
+ uint16_t sdo_count;
+ uint32_t sii_nwords;
+ char group[EC_IOCTL_STRING_SIZE];
+ char image[EC_IOCTL_STRING_SIZE];
+ char order[EC_IOCTL_STRING_SIZE];
+ char name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_slave_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint32_t sync_index;
+
+ // outputs
+ uint16_t physical_start_address;
+ uint16_t default_size;
+ uint8_t control_register;
+ uint8_t enable;
+ uint8_t pdo_count;
+} ec_ioctl_slave_sync_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint32_t sync_index;
+ uint32_t pdo_pos;
+
+ // outputs
+ uint16_t index;
+ uint8_t entry_count;
+ int8_t name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_slave_sync_pdo_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint32_t sync_index;
+ uint32_t pdo_pos;
+ uint32_t entry_pos;
+
+ // outputs
+ uint16_t index;
+ uint8_t subindex;
+ uint8_t bit_length;
+ int8_t name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_slave_sync_pdo_entry_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t index;
+
+ // outputs
+ uint32_t data_size;
+ uint32_t logical_base_address;
+ uint16_t working_counter[EC_MAX_NUM_DEVICES];
+ uint16_t expected_working_counter;
+ uint32_t fmmu_count;
+} ec_ioctl_domain_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t domain_index;
+ uint32_t fmmu_index;
+
+ // outputs
+ uint16_t slave_config_alias;
+ uint16_t slave_config_position;
+ uint8_t sync_index;
+ ec_direction_t dir;
+ uint32_t logical_address;
+ uint32_t data_size;
+} ec_ioctl_domain_fmmu_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t domain_index;
+ uint32_t data_size;
+ uint8_t *target;
+} ec_ioctl_domain_data_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint8_t al_state;
+} ec_ioctl_slave_state_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint16_t sdo_position;
+
+ // outputs
+ uint16_t sdo_index;
+ uint8_t max_subindex;
+ int8_t name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_slave_sdo_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ int sdo_spec; // positive: index, negative: list position
+ uint8_t sdo_entry_subindex;
+
+ // outputs
+ uint16_t data_type;
+ uint16_t bit_length;
+ uint8_t read_access[EC_SDO_ENTRY_ACCESS_COUNT];
+ uint8_t write_access[EC_SDO_ENTRY_ACCESS_COUNT];
+ int8_t description[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_slave_sdo_entry_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint16_t sdo_index;
+ uint8_t sdo_entry_subindex;
+ size_t target_size;
+ uint8_t *target;
+
+ // outputs
+ size_t data_size;
+ uint32_t abort_code;
+} ec_ioctl_slave_sdo_upload_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint16_t sdo_index;
+ uint8_t sdo_entry_subindex;
+ uint8_t complete_access;
+ size_t data_size;
+ uint8_t *data;
+
+ // outputs
+ uint32_t abort_code;
+} ec_ioctl_slave_sdo_download_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint16_t offset;
+ uint32_t nwords;
+ uint16_t *words;
+} ec_ioctl_slave_sii_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint8_t emergency;
+ uint16_t address;
+ size_t size;
+ uint8_t *data;
+} ec_ioctl_slave_reg_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint16_t offset;
+ size_t buffer_size;
+ uint8_t *buffer;
+
+ // outputs
+ size_t data_size;
+ uint32_t result;
+ uint32_t error_code;
+ char file_name[32];
+} ec_ioctl_slave_foe_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint8_t drive_no;
+ uint16_t idn;
+ size_t mem_size;
+ uint8_t *data;
+
+ // outputs
+ size_t data_size;
+ uint16_t error_code;
+} ec_ioctl_slave_soe_read_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint16_t slave_position;
+ uint8_t drive_no;
+ uint16_t idn;
+ size_t data_size;
+ uint8_t *data;
+
+ // outputs
+ uint16_t error_code;
+} ec_ioctl_slave_soe_write_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+
+ // outputs
+ uint16_t alias;
+ uint16_t position;
+ uint32_t vendor_id;
+ uint32_t product_code;
+ struct {
+ ec_direction_t dir;
+ ec_watchdog_mode_t watchdog_mode;
+ uint32_t pdo_count;
+ uint8_t config_this;
+ } syncs[EC_MAX_SYNC_MANAGERS];
+ uint16_t watchdog_divider;
+ uint16_t watchdog_intervals;
+ uint32_t sdo_count;
+ uint32_t idn_count;
+ int32_t slave_position;
+ uint16_t dc_assign_activate;
+ ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT];
+} ec_ioctl_config_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint8_t sync_index;
+ uint16_t pdo_pos;
+
+ // outputs
+ uint16_t index;
+ uint8_t entry_count;
+ int8_t name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_config_pdo_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint8_t sync_index;
+ uint16_t pdo_pos;
+ uint8_t entry_pos;
+
+ // outputs
+ uint16_t index;
+ uint8_t subindex;
+ uint8_t bit_length;
+ int8_t name[EC_IOCTL_STRING_SIZE];
+} ec_ioctl_config_pdo_entry_t;
+
+/*****************************************************************************/
+
+/** Maximum size for displayed SDO data.
+ * \todo Make this dynamic.
+ */
+#define EC_MAX_SDO_DATA_SIZE 1024
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint32_t sdo_pos;
+
+ // outputs
+ uint16_t index;
+ uint8_t subindex;
+ size_t size;
+ uint8_t data[EC_MAX_SDO_DATA_SIZE];
+ uint8_t complete_access;
+} ec_ioctl_config_sdo_t;
+
+/*****************************************************************************/
+
+/** Maximum size for displayed IDN data.
+ * \todo Make this dynamic.
+ */
+#define EC_MAX_IDN_DATA_SIZE 1024
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint32_t idn_pos;
+
+ // outputs
+ uint8_t drive_no;
+ uint16_t idn;
+ ec_al_state_t state;
+ size_t size;
+ uint8_t data[EC_MAX_IDN_DATA_SIZE];
+} ec_ioctl_config_idn_t;
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+
+typedef struct {
+ // input
+ uint16_t eoe_index;
+
+ // outputs
+ char name[EC_DATAGRAM_NAME_SIZE];
+ uint16_t slave_position;
+ uint8_t open;
+ uint32_t rx_bytes;
+ uint32_t rx_rate;
+ uint32_t tx_bytes;
+ uint32_t tx_rate;
+ uint32_t tx_queued_frames;
+ uint32_t tx_queue_size;
+} ec_ioctl_eoe_handler_t;
+
+#endif
+
+/*****************************************************************************/
+
+typedef struct {
+ // outputs
+ void *process_data;
+ size_t process_data_size;
+} ec_ioctl_master_activate_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint16_t pdo_index;
+ uint16_t entry_index;
+ uint8_t entry_subindex;
+ uint8_t entry_bit_length;
+} ec_ioctl_add_pdo_entry_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint16_t entry_index;
+ uint8_t entry_subindex;
+ uint32_t domain_index;
+
+ // outputs
+ unsigned int bit_position;
+} ec_ioctl_reg_pdo_entry_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint32_t sync_index;
+ uint32_t pdo_pos;
+ uint32_t entry_pos;
+ uint32_t domain_index;
+
+ // outputs
+ unsigned int bit_position;
+} ec_ioctl_reg_pdo_pos_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint16_t index;
+ uint8_t subindex;
+ const uint8_t *data;
+ size_t size;
+ uint8_t complete_access;
+} ec_ioctl_sc_sdo_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ size_t size;
+ uint8_t *target;
+
+ // outputs
+ int32_t overruns;
+} ec_ioctl_sc_emerg_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+
+ // outputs
+ ec_slave_config_state_t *state;
+} ec_ioctl_sc_state_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ uint8_t drive_no;
+ uint16_t idn;
+ ec_al_state_t al_state;
+ const uint8_t *data;
+ size_t size;
+} ec_ioctl_sc_idn_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t domain_index;
+
+ // outputs
+ ec_domain_state_t *state;
+} ec_ioctl_domain_state_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+
+ // inputs/outputs
+ uint32_t request_index;
+ uint16_t sdo_index;
+ uint8_t sdo_subindex;
+ size_t size;
+ uint8_t *data;
+ uint32_t timeout;
+ ec_request_state_t state;
+} ec_ioctl_sdo_request_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+ size_t mem_size;
+
+ // inputs/outputs
+ uint32_t request_index;
+ uint8_t *data;
+ ec_request_state_t state;
+ uint8_t new_data;
+ uint16_t address;
+ size_t transfer_size;
+} ec_ioctl_reg_request_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t config_index;
+
+ // inputs/outputs
+ uint32_t voe_index;
+ uint32_t *vendor_id;
+ uint16_t *vendor_type;
+ size_t size;
+ uint8_t *data;
+ ec_request_state_t state;
+} ec_ioctl_voe_t;
+
+/*****************************************************************************/
+
+typedef struct {
+ // inputs
+ uint32_t dev_idx;
+
+ // outputs
+ ec_master_link_state_t *state;
+} ec_ioctl_link_state_t;
+
+/*****************************************************************************/
+
+#ifdef __KERNEL__
+
+/** Context data structure for file handles.
+ */
+typedef struct {
+ unsigned int writable; /**< Device was opened with write permission. */
+ unsigned int requested; /**< Master was requested via this file handle. */
+ uint8_t *process_data; /**< Total process data area. */
+ size_t process_data_size; /**< Size of the \a process_data. */
+} ec_ioctl_context_t;
+
+long ec_ioctl(ec_master_t *, ec_ioctl_context_t *, unsigned int,
+ void __user *);
+
+#ifdef EC_RTDM
+
+long ec_ioctl_rtdm(ec_master_t *, ec_ioctl_context_t *, unsigned int,
+ void __user *);
+int ec_rtdm_mmap(ec_ioctl_context_t *, void **);
+
+#endif
+
+#endif
+
+/*****************************************************************************/
+
+/** \endcond */
+
+#endif
diff --git a/net/ethercat/master/mailbox.c b/net/ethercat/master/mailbox.c
new file mode 100644
index 000000000000..10f47698b69f
--- /dev/null
+++ b/net/ethercat/master/mailbox.c
@@ -0,0 +1,210 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Mailbox functionality.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+#include "mailbox.h"
+#include "datagram.h"
+#include "master.h"
+
+/*****************************************************************************/
+
+/**
+ Prepares a mailbox-send datagram.
+ \return Pointer to mailbox datagram data, or ERR_PTR() code.
+*/
+
+uint8_t *ec_slave_mbox_prepare_send(const ec_slave_t *slave, /**< slave */
+ ec_datagram_t *datagram, /**< datagram */
+ uint8_t type, /**< mailbox protocol */
+ size_t size /**< size of the data */
+ )
+{
+ size_t total_size;
+ int ret;
+
+ if (unlikely(!slave->sii.mailbox_protocols)) {
+ EC_SLAVE_ERR(slave, "Slave does not support mailbox"
+ " communication!\n");
+ return ERR_PTR(-EPROTONOSUPPORT);
+ }
+
+ total_size = EC_MBOX_HEADER_SIZE + size;
+
+ if (unlikely(total_size > slave->configured_rx_mailbox_size)) {
+ EC_SLAVE_ERR(slave, "Data size (%zu) does not fit in mailbox (%u)!\n",
+ total_size, slave->configured_rx_mailbox_size);
+ return ERR_PTR(-EOVERFLOW);
+ }
+
+ ret = ec_datagram_fpwr(datagram, slave->station_address,
+ slave->configured_rx_mailbox_offset,
+ slave->configured_rx_mailbox_size);
+ if (ret)
+ return ERR_PTR(ret);
+
+ EC_WRITE_U16(datagram->data, size); // mailbox service data length
+ EC_WRITE_U16(datagram->data + 2, slave->station_address); // station addr.
+ EC_WRITE_U8 (datagram->data + 4, 0x00); // channel & priority
+ EC_WRITE_U8 (datagram->data + 5, type); // underlying protocol type
+
+ return datagram->data + EC_MBOX_HEADER_SIZE;
+}
+
+/*****************************************************************************/
+
+/**
+ Prepares a datagram for checking the mailbox state.
+ \todo Determine sync manager used for receive mailbox
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_mbox_prepare_check(const ec_slave_t *slave, /**< slave */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ int ret = ec_datagram_fprd(datagram, slave->station_address, 0x808, 8);
+ if (ret)
+ return ret;
+
+ ec_datagram_zero(datagram);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Processes a mailbox state checking datagram.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_mbox_check(const ec_datagram_t *datagram /**< datagram */)
+{
+ return EC_READ_U8(datagram->data + 5) & 8 ? 1 : 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Prepares a datagram to fetch mailbox data.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_mbox_prepare_fetch(const ec_slave_t *slave, /**< slave */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ int ret = ec_datagram_fprd(datagram, slave->station_address,
+ slave->configured_tx_mailbox_offset,
+ slave->configured_tx_mailbox_size);
+ if (ret)
+ return ret;
+
+ ec_datagram_zero(datagram);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Mailbox error codes.
+*/
+
+const ec_code_msg_t mbox_error_messages[] = {
+ {0x00000001, "MBXERR_SYNTAX"},
+ {0x00000002, "MBXERR_UNSUPPORTEDPROTOCOL"},
+ {0x00000003, "MBXERR_INVAILDCHANNEL"},
+ {0x00000004, "MBXERR_SERVICENOTSUPPORTED"},
+ {0x00000005, "MBXERR_INVALIDHEADER"},
+ {0x00000006, "MBXERR_SIZETOOSHORT"},
+ {0x00000007, "MBXERR_NOMOREMEMORY"},
+ {0x00000008, "MBXERR_INVALIDSIZE"},
+ {}
+};
+
+/*****************************************************************************/
+
+/** Processes received mailbox data.
+ *
+ * \return Pointer to the received data, or ERR_PTR() code.
+ */
+uint8_t *ec_slave_mbox_fetch(const ec_slave_t *slave, /**< slave */
+ const ec_datagram_t *datagram, /**< datagram */
+ uint8_t *type, /**< expected mailbox protocol */
+ size_t *size /**< size of the received data */
+ )
+{
+ size_t data_size;
+
+ data_size = EC_READ_U16(datagram->data);
+
+ if (data_size + EC_MBOX_HEADER_SIZE > slave->configured_tx_mailbox_size) {
+ EC_SLAVE_ERR(slave, "Corrupt mailbox response received!\n");
+ ec_print_data(datagram->data, slave->configured_tx_mailbox_size);
+ return ERR_PTR(-EPROTO);
+ }
+
+ *type = EC_READ_U8(datagram->data + 5) & 0x0F;
+ *size = data_size;
+
+ if (*type == 0x00) {
+ const ec_code_msg_t *mbox_msg;
+ uint16_t code = EC_READ_U16(datagram->data + 8);
+
+ EC_SLAVE_ERR(slave, "Mailbox error response received - ");
+
+ for (mbox_msg = mbox_error_messages; mbox_msg->code; mbox_msg++) {
+ if (mbox_msg->code != code)
+ continue;
+ printk("Code 0x%04X: \"%s\".\n",
+ mbox_msg->code, mbox_msg->message);
+ break;
+ }
+
+ if (!mbox_msg->code)
+ printk("Unknown error reply code 0x%04X.\n", code);
+
+ if (slave->master->debug_level)
+ ec_print_data(datagram->data + EC_MBOX_HEADER_SIZE, data_size);
+
+ return ERR_PTR(-EPROTO);
+ }
+
+ return datagram->data + EC_MBOX_HEADER_SIZE;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/mailbox.h b/net/ethercat/master/mailbox.h
new file mode 100644
index 000000000000..cdd2eb250a1c
--- /dev/null
+++ b/net/ethercat/master/mailbox.h
@@ -0,0 +1,60 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Mailbox functionality.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_MAILBOX_H__
+#define __EC_MAILBOX_H__
+
+#include "slave.h"
+
+/*****************************************************************************/
+
+/** Size of the mailbox header.
+ */
+#define EC_MBOX_HEADER_SIZE 6
+
+/*****************************************************************************/
+
+uint8_t *ec_slave_mbox_prepare_send(const ec_slave_t *, ec_datagram_t *,
+ uint8_t, size_t);
+int ec_slave_mbox_prepare_check(const ec_slave_t *, ec_datagram_t *);
+int ec_slave_mbox_check(const ec_datagram_t *);
+int ec_slave_mbox_prepare_fetch(const ec_slave_t *, ec_datagram_t *);
+uint8_t *ec_slave_mbox_fetch(const ec_slave_t *, const ec_datagram_t *,
+ uint8_t *, size_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/master.c b/net/ethercat/master/master.c
new file mode 100644
index 000000000000..574862d7d316
--- /dev/null
+++ b/net/ethercat/master/master.c
@@ -0,0 +1,3321 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2006-2020 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
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT master methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/version.h>
+#include <linux/hrtimer.h>
+
+#include "globals.h"
+#include "slave.h"
+#include "slave_config.h"
+#include "device.h"
+#include "datagram.h"
+
+#ifdef EC_EOE
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0)
+#include <uapi/linux/sched/types.h> // struct sched_param
+#include <linux/sched/types.h> // sched_setscheduler
+#endif
+#include "ethernet.h"
+#endif
+
+#include "master.h"
+
+/*****************************************************************************/
+
+/** Set to 1 to enable external datagram injection debugging.
+ */
+#define DEBUG_INJECT 0
+
+/** Always output corrupted frames.
+ */
+#define FORCE_OUTPUT_CORRUPTED 0
+
+#ifdef EC_HAVE_CYCLES
+
+/** Frame timeout in cycles.
+ */
+static cycles_t timeout_cycles;
+
+/** Timeout for external datagram injection [cycles].
+ */
+static cycles_t ext_injection_timeout_cycles;
+
+#else
+
+/** Frame timeout in jiffies.
+ */
+static unsigned long timeout_jiffies;
+
+/** Timeout for external datagram injection [jiffies].
+ */
+static unsigned long ext_injection_timeout_jiffies;
+
+#endif
+
+/** List of intervals for statistics [s].
+ */
+const unsigned int rate_intervals[] = {
+ 1, 10, 60
+};
+
+/*****************************************************************************/
+
+void ec_master_clear_slave_configs(ec_master_t *);
+void ec_master_clear_domains(ec_master_t *);
+static int ec_master_idle_thread(void *);
+static int ec_master_operation_thread(void *);
+#ifdef EC_EOE
+static int ec_master_eoe_thread(void *);
+#endif
+void ec_master_find_dc_ref_clock(ec_master_t *);
+void ec_master_clear_device_stats(ec_master_t *);
+void ec_master_update_device_stats(ec_master_t *);
+
+/*****************************************************************************/
+
+/** Static variables initializer.
+*/
+void ec_master_init_static(void)
+{
+#ifdef EC_HAVE_CYCLES
+ timeout_cycles = (cycles_t) EC_IO_TIMEOUT /* us */ * (cpu_khz / 1000);
+ ext_injection_timeout_cycles =
+ (cycles_t) EC_SDO_INJECTION_TIMEOUT /* us */ * (cpu_khz / 1000);
+#else
+ // one jiffy may always elapse between time measurement
+ timeout_jiffies = max(EC_IO_TIMEOUT * HZ / 1000000, 1);
+ ext_injection_timeout_jiffies =
+ max(EC_SDO_INJECTION_TIMEOUT * HZ / 1000000, 1);
+#endif
+}
+
+/*****************************************************************************/
+
+/**
+ Master constructor.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_master_init(ec_master_t *master, /**< EtherCAT master */
+ unsigned int index, /**< master index */
+ const uint8_t *main_mac, /**< MAC address of main device */
+ const uint8_t *backup_mac, /**< MAC address of backup device */
+ dev_t device_number, /**< Character device number. */
+ struct class *class, /**< Device class. */
+ unsigned int debug_level /**< Debug level (module parameter). */
+ )
+{
+ int ret;
+ unsigned int dev_idx, i;
+
+ master->index = index;
+ master->reserved = 0;
+
+ sema_init(&master->master_sem, 1);
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < EC_MAX_NUM_DEVICES; dev_idx++) {
+ master->macs[dev_idx] = NULL;
+ }
+
+ master->macs[EC_DEVICE_MAIN] = main_mac;
+
+#if EC_MAX_NUM_DEVICES > 1
+ master->macs[EC_DEVICE_BACKUP] = backup_mac;
+ master->num_devices = 1 + !ec_mac_is_zero(backup_mac);
+#else
+ if (!ec_mac_is_zero(backup_mac)) {
+ EC_MASTER_WARN(master, "Ignoring backup MAC address!");
+ }
+#endif
+
+ ec_master_clear_device_stats(master);
+
+ sema_init(&master->device_sem, 1);
+
+ master->phase = EC_ORPHANED;
+ master->active = 0;
+ master->config_changed = 0;
+ master->injection_seq_fsm = 0;
+ master->injection_seq_rt = 0;
+
+ master->slaves = NULL;
+ master->slave_count = 0;
+
+ INIT_LIST_HEAD(&master->configs);
+ INIT_LIST_HEAD(&master->domains);
+
+ master->app_time = 0ULL;
+ master->dc_ref_time = 0ULL;
+
+ master->scan_busy = 0;
+ master->allow_scan = 1;
+ sema_init(&master->scan_sem, 1);
+ init_waitqueue_head(&master->scan_queue);
+
+ master->config_busy = 0;
+ sema_init(&master->config_sem, 1);
+ init_waitqueue_head(&master->config_queue);
+
+ INIT_LIST_HEAD(&master->datagram_queue);
+ master->datagram_index = 0;
+
+ INIT_LIST_HEAD(&master->ext_datagram_queue);
+ sema_init(&master->ext_queue_sem, 1);
+
+ master->ext_ring_idx_rt = 0;
+ master->ext_ring_idx_fsm = 0;
+
+ // init external datagram ring
+ for (i = 0; i < EC_EXT_RING_SIZE; i++) {
+ ec_datagram_t *datagram = &master->ext_datagram_ring[i];
+ ec_datagram_init(datagram);
+ snprintf(datagram->name, EC_DATAGRAM_NAME_SIZE, "ext-%u", i);
+ }
+
+ // send interval in IDLE phase
+ ec_master_set_send_interval(master, 1000000 / HZ);
+
+ master->fsm_slave = NULL;
+ INIT_LIST_HEAD(&master->fsm_exec_list);
+ master->fsm_exec_count = 0U;
+
+ master->debug_level = debug_level;
+ master->stats.timeouts = 0;
+ master->stats.corrupted = 0;
+ master->stats.unmatched = 0;
+ master->stats.output_jiffies = 0;
+
+ master->thread = NULL;
+
+#ifdef EC_EOE
+ master->eoe_thread = NULL;
+ INIT_LIST_HEAD(&master->eoe_handlers);
+#endif
+
+ sema_init(&master->io_sem, 1);
+ master->send_cb = NULL;
+ master->receive_cb = NULL;
+ master->cb_data = NULL;
+ master->app_send_cb = NULL;
+ master->app_receive_cb = NULL;
+ master->app_cb_data = NULL;
+
+ INIT_LIST_HEAD(&master->sii_requests);
+ INIT_LIST_HEAD(&master->emerg_reg_requests);
+
+ init_waitqueue_head(&master->request_queue);
+
+ // init devices
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ ret = ec_device_init(&master->devices[dev_idx], master);
+ if (ret < 0) {
+ goto out_clear_devices;
+ }
+ }
+
+ // init state machine datagram
+ ec_datagram_init(&master->fsm_datagram);
+ snprintf(master->fsm_datagram.name, EC_DATAGRAM_NAME_SIZE, "master-fsm");
+ ret = ec_datagram_prealloc(&master->fsm_datagram, EC_MAX_DATA_SIZE);
+ if (ret < 0) {
+ ec_datagram_clear(&master->fsm_datagram);
+ EC_MASTER_ERR(master, "Failed to allocate FSM datagram.\n");
+ goto out_clear_devices;
+ }
+
+ // create state machine object
+ ec_fsm_master_init(&master->fsm, master, &master->fsm_datagram);
+
+ // alloc external datagram ring
+ for (i = 0; i < EC_EXT_RING_SIZE; i++) {
+ ec_datagram_t *datagram = &master->ext_datagram_ring[i];
+ ret = ec_datagram_prealloc(datagram, EC_MAX_DATA_SIZE);
+ if (ret) {
+ EC_MASTER_ERR(master, "Failed to allocate external"
+ " datagram %u.\n", i);
+ goto out_clear_ext_datagrams;
+ }
+ }
+
+ // init reference sync datagram
+ ec_datagram_init(&master->ref_sync_datagram);
+ snprintf(master->ref_sync_datagram.name, EC_DATAGRAM_NAME_SIZE,
+ "refsync");
+ ret = ec_datagram_prealloc(&master->ref_sync_datagram, 4);
+ if (ret < 0) {
+ ec_datagram_clear(&master->ref_sync_datagram);
+ EC_MASTER_ERR(master, "Failed to allocate reference"
+ " synchronisation datagram.\n");
+ goto out_clear_ext_datagrams;
+ }
+
+ // init sync datagram
+ ec_datagram_init(&master->sync_datagram);
+ snprintf(master->sync_datagram.name, EC_DATAGRAM_NAME_SIZE, "sync");
+ ret = ec_datagram_prealloc(&master->sync_datagram, 4);
+ if (ret < 0) {
+ ec_datagram_clear(&master->sync_datagram);
+ EC_MASTER_ERR(master, "Failed to allocate"
+ " synchronisation datagram.\n");
+ goto out_clear_ref_sync;
+ }
+
+ // init sync monitor datagram
+ ec_datagram_init(&master->sync_mon_datagram);
+ snprintf(master->sync_mon_datagram.name, EC_DATAGRAM_NAME_SIZE,
+ "syncmon");
+ ret = ec_datagram_brd(&master->sync_mon_datagram, 0x092c, 4);
+ if (ret < 0) {
+ ec_datagram_clear(&master->sync_mon_datagram);
+ EC_MASTER_ERR(master, "Failed to allocate sync"
+ " monitoring datagram.\n");
+ goto out_clear_sync;
+ }
+
+ master->dc_ref_config = NULL;
+ master->dc_ref_clock = NULL;
+
+ // init character device
+ ret = ec_cdev_init(&master->cdev, master, device_number);
+ if (ret)
+ goto out_clear_sync_mon;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+ master->class_device = device_create(class, NULL,
+ MKDEV(MAJOR(device_number), master->index), NULL,
+ "EtherCAT%u", master->index);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ master->class_device = device_create(class, NULL,
+ MKDEV(MAJOR(device_number), master->index),
+ "EtherCAT%u", master->index);
+#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 15)
+ master->class_device = class_device_create(class, NULL,
+ MKDEV(MAJOR(device_number), master->index), NULL,
+ "EtherCAT%u", master->index);
+#else
+ master->class_device = class_device_create(class,
+ MKDEV(MAJOR(device_number), master->index), NULL,
+ "EtherCAT%u", master->index);
+#endif
+ if (IS_ERR(master->class_device)) {
+ EC_MASTER_ERR(master, "Failed to create class device!\n");
+ ret = PTR_ERR(master->class_device);
+ goto out_clear_cdev;
+ }
+
+#ifdef EC_RTDM
+ // init RTDM device
+ ret = ec_rtdm_dev_init(&master->rtdm_dev, master);
+ if (ret) {
+ goto out_unregister_class_device;
+ }
+#endif
+
+ return 0;
+
+#ifdef EC_RTDM
+out_unregister_class_device:
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ device_unregister(master->class_device);
+#else
+ class_device_unregister(master->class_device);
+#endif
+#endif
+out_clear_cdev:
+ ec_cdev_clear(&master->cdev);
+out_clear_sync_mon:
+ ec_datagram_clear(&master->sync_mon_datagram);
+out_clear_sync:
+ ec_datagram_clear(&master->sync_datagram);
+out_clear_ref_sync:
+ ec_datagram_clear(&master->ref_sync_datagram);
+out_clear_ext_datagrams:
+ for (i = 0; i < EC_EXT_RING_SIZE; i++) {
+ ec_datagram_clear(&master->ext_datagram_ring[i]);
+ }
+ ec_fsm_master_clear(&master->fsm);
+ ec_datagram_clear(&master->fsm_datagram);
+out_clear_devices:
+ for (; dev_idx > 0; dev_idx--) {
+ ec_device_clear(&master->devices[dev_idx - 1]);
+ }
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+*/
+void ec_master_clear(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ unsigned int dev_idx, i;
+
+#ifdef EC_RTDM
+ ec_rtdm_dev_clear(&master->rtdm_dev);
+#endif
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ device_unregister(master->class_device);
+#else
+ class_device_unregister(master->class_device);
+#endif
+
+ ec_cdev_clear(&master->cdev);
+
+#ifdef EC_EOE
+ ec_master_clear_eoe_handlers(master);
+#endif
+ ec_master_clear_domains(master);
+ ec_master_clear_slave_configs(master);
+ ec_master_clear_slaves(master);
+
+ ec_datagram_clear(&master->sync_mon_datagram);
+ ec_datagram_clear(&master->sync_datagram);
+ ec_datagram_clear(&master->ref_sync_datagram);
+
+ for (i = 0; i < EC_EXT_RING_SIZE; i++) {
+ ec_datagram_clear(&master->ext_datagram_ring[i]);
+ }
+
+ ec_fsm_master_clear(&master->fsm);
+ ec_datagram_clear(&master->fsm_datagram);
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ ec_device_clear(&master->devices[dev_idx]);
+ }
+}
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+/** Clear and free all EoE handlers.
+ */
+void ec_master_clear_eoe_handlers(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ ec_eoe_t *eoe, *next;
+
+ list_for_each_entry_safe(eoe, next, &master->eoe_handlers, list) {
+ list_del(&eoe->list);
+ ec_eoe_clear(eoe);
+ kfree(eoe);
+ }
+}
+#endif
+
+/*****************************************************************************/
+
+/** Clear all slave configurations.
+ */
+void ec_master_clear_slave_configs(ec_master_t *master)
+{
+ ec_slave_config_t *sc, *next;
+
+ master->dc_ref_config = NULL;
+ master->fsm.sdo_request = NULL; // mark sdo_request as invalid
+
+ list_for_each_entry_safe(sc, next, &master->configs, list) {
+ list_del(&sc->list);
+ ec_slave_config_clear(sc);
+ kfree(sc);
+ }
+}
+
+/*****************************************************************************/
+
+/** Clear all slaves.
+ */
+void ec_master_clear_slaves(ec_master_t *master)
+{
+ ec_slave_t *slave;
+
+ master->dc_ref_clock = NULL;
+
+ // External requests are obsolete, so we wake pending waiters and remove
+ // them from the list.
+
+ while (!list_empty(&master->sii_requests)) {
+ ec_sii_write_request_t *request =
+ list_entry(master->sii_requests.next,
+ ec_sii_write_request_t, list);
+ list_del_init(&request->list); // dequeue
+ EC_MASTER_WARN(master, "Discarding SII request, slave %u about"
+ " to be deleted.\n", request->slave->ring_position);
+ request->state = EC_INT_REQUEST_FAILURE;
+ wake_up_all(&master->request_queue);
+ }
+
+ master->fsm_slave = NULL;
+ INIT_LIST_HEAD(&master->fsm_exec_list);
+ master->fsm_exec_count = 0;
+
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ ec_slave_clear(slave);
+ }
+
+ if (master->slaves) {
+ kfree(master->slaves);
+ master->slaves = NULL;
+ }
+
+ master->slave_count = 0;
+}
+
+/*****************************************************************************/
+
+/** Clear all domains.
+ */
+void ec_master_clear_domains(ec_master_t *master)
+{
+ ec_domain_t *domain, *next;
+
+ list_for_each_entry_safe(domain, next, &master->domains, list) {
+ list_del(&domain->list);
+ ec_domain_clear(domain);
+ kfree(domain);
+ }
+}
+
+/*****************************************************************************/
+
+/** Clear the configuration applied by the application.
+ */
+void ec_master_clear_config(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ down(&master->master_sem);
+ ec_master_clear_domains(master);
+ ec_master_clear_slave_configs(master);
+ up(&master->master_sem);
+}
+
+/*****************************************************************************/
+
+/** Internal sending callback.
+ */
+void ec_master_internal_send_cb(
+ void *cb_data /**< Callback data. */
+ )
+{
+ ec_master_t *master = (ec_master_t *) cb_data;
+ down(&master->io_sem);
+ ecrt_master_send_ext(master);
+ up(&master->io_sem);
+}
+
+/*****************************************************************************/
+
+/** Internal receiving callback.
+ */
+void ec_master_internal_receive_cb(
+ void *cb_data /**< Callback data. */
+ )
+{
+ ec_master_t *master = (ec_master_t *) cb_data;
+ down(&master->io_sem);
+ ecrt_master_receive(master);
+ up(&master->io_sem);
+}
+
+/*****************************************************************************/
+
+/** Starts the master thread.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_master_thread_start(
+ ec_master_t *master, /**< EtherCAT master */
+ int (*thread_func)(void *), /**< thread function to start */
+ const char *name /**< Thread name. */
+ )
+{
+ EC_MASTER_INFO(master, "Starting %s thread.\n", name);
+ master->thread = kthread_run(thread_func, master, name);
+ if (IS_ERR(master->thread)) {
+ int err = (int) PTR_ERR(master->thread);
+ EC_MASTER_ERR(master, "Failed to start master thread (error %i)!\n",
+ err);
+ master->thread = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Stops the master thread.
+ */
+void ec_master_thread_stop(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ unsigned long sleep_jiffies;
+
+ if (!master->thread) {
+ EC_MASTER_WARN(master, "%s(): Already finished!\n", __func__);
+ return;
+ }
+
+ EC_MASTER_DBG(master, 1, "Stopping master thread.\n");
+
+ kthread_stop(master->thread);
+ master->thread = NULL;
+ EC_MASTER_INFO(master, "Master thread exited.\n");
+
+ if (master->fsm_datagram.state != EC_DATAGRAM_SENT) {
+ return;
+ }
+
+ // wait for FSM datagram
+ sleep_jiffies = max(HZ / 100, 1); // 10 ms, at least 1 jiffy
+ schedule_timeout(sleep_jiffies);
+}
+
+/*****************************************************************************/
+
+/** Transition function from ORPHANED to IDLE phase.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_master_enter_idle_phase(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ int ret;
+ ec_device_index_t dev_idx;
+
+ EC_MASTER_DBG(master, 1, "ORPHANED -> IDLE.\n");
+
+ master->send_cb = ec_master_internal_send_cb;
+ master->receive_cb = ec_master_internal_receive_cb;
+ master->cb_data = master;
+
+ master->phase = EC_IDLE;
+
+ // reset number of responding slaves to trigger scanning
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ master->fsm.slaves_responding[dev_idx] = 0;
+ }
+
+ ret = ec_master_thread_start(master, ec_master_idle_thread,
+ "EtherCAT-IDLE");
+ if (ret)
+ master->phase = EC_ORPHANED;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Transition function from IDLE to ORPHANED phase.
+ */
+void ec_master_leave_idle_phase(ec_master_t *master /**< EtherCAT master */)
+{
+ EC_MASTER_DBG(master, 1, "IDLE -> ORPHANED.\n");
+
+ master->phase = EC_ORPHANED;
+
+#ifdef EC_EOE
+ ec_master_eoe_stop(master);
+#endif
+ ec_master_thread_stop(master);
+
+ down(&master->master_sem);
+ ec_master_clear_slaves(master);
+ up(&master->master_sem);
+
+ ec_fsm_master_reset(&master->fsm);
+}
+
+/*****************************************************************************/
+
+/** Transition function from IDLE to OPERATION phase.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_master_enter_operation_phase(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ int ret = 0;
+ ec_slave_t *slave;
+#ifdef EC_EOE
+ ec_eoe_t *eoe;
+#endif
+
+ EC_MASTER_DBG(master, 1, "IDLE -> OPERATION.\n");
+
+ down(&master->config_sem);
+ if (master->config_busy) {
+ up(&master->config_sem);
+
+ // wait for slave configuration to complete
+ ret = wait_event_interruptible(master->config_queue,
+ !master->config_busy);
+ if (ret) {
+ EC_MASTER_INFO(master, "Finishing slave configuration"
+ " interrupted by signal.\n");
+ goto out_return;
+ }
+
+ EC_MASTER_DBG(master, 1, "Waiting for pending slave"
+ " configuration returned.\n");
+ } else {
+ up(&master->config_sem);
+ }
+
+ down(&master->scan_sem);
+ master->allow_scan = 0; // 'lock' the slave list
+ if (!master->scan_busy) {
+ up(&master->scan_sem);
+ } else {
+ up(&master->scan_sem);
+
+ // wait for slave scan to complete
+ ret = wait_event_interruptible(master->scan_queue,
+ !master->scan_busy);
+ if (ret) {
+ EC_MASTER_INFO(master, "Waiting for slave scan"
+ " interrupted by signal.\n");
+ goto out_allow;
+ }
+
+ EC_MASTER_DBG(master, 1, "Waiting for pending"
+ " slave scan returned.\n");
+ }
+
+ // set states for all slaves
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+ }
+
+#ifdef EC_EOE
+ // ... but set EoE slaves to OP
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ if (ec_eoe_is_open(eoe))
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
+ }
+#endif
+
+ master->phase = EC_OPERATION;
+ master->app_send_cb = NULL;
+ master->app_receive_cb = NULL;
+ master->app_cb_data = NULL;
+ return ret;
+
+out_allow:
+ master->allow_scan = 1;
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Transition function from OPERATION to IDLE phase.
+ */
+void ec_master_leave_operation_phase(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ if (master->active) {
+ ecrt_master_deactivate(master); // also clears config
+ } else {
+ ec_master_clear_config(master);
+ }
+
+ /* Re-allow scanning for IDLE phase. */
+ master->allow_scan = 1;
+
+ EC_MASTER_DBG(master, 1, "OPERATION -> IDLE.\n");
+
+ master->phase = EC_IDLE;
+}
+
+/*****************************************************************************/
+
+/** Injects external datagrams that fit into the datagram queue.
+ */
+void ec_master_inject_external_datagrams(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ ec_datagram_t *datagram;
+ size_t queue_size = 0, new_queue_size = 0;
+#if DEBUG_INJECT
+ unsigned int datagram_count = 0;
+#endif
+
+ if (master->ext_ring_idx_rt == master->ext_ring_idx_fsm) {
+ // nothing to inject
+ return;
+ }
+
+ list_for_each_entry(datagram, &master->datagram_queue, queue) {
+ if (datagram->state == EC_DATAGRAM_QUEUED) {
+ queue_size += datagram->data_size;
+ }
+ }
+
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "Injecting datagrams, queue_size=%zu\n",
+ queue_size);
+#endif
+
+ while (master->ext_ring_idx_rt != master->ext_ring_idx_fsm) {
+ datagram = &master->ext_datagram_ring[master->ext_ring_idx_rt];
+
+ if (datagram->state != EC_DATAGRAM_INIT) {
+ // skip datagram
+ master->ext_ring_idx_rt =
+ (master->ext_ring_idx_rt + 1) % EC_EXT_RING_SIZE;
+ continue;
+ }
+
+ new_queue_size = queue_size + datagram->data_size;
+ if (new_queue_size <= master->max_queue_size) {
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "Injecting datagram %s"
+ " size=%zu, queue_size=%zu\n", datagram->name,
+ datagram->data_size, new_queue_size);
+ datagram_count++;
+#endif
+#ifdef EC_HAVE_CYCLES
+ datagram->cycles_sent = 0;
+#endif
+ datagram->jiffies_sent = 0;
+ ec_master_queue_datagram(master, datagram);
+ queue_size = new_queue_size;
+ }
+ else if (datagram->data_size > master->max_queue_size) {
+ datagram->state = EC_DATAGRAM_ERROR;
+ EC_MASTER_ERR(master, "External datagram %s is too large,"
+ " size=%zu, max_queue_size=%zu\n",
+ datagram->name, datagram->data_size,
+ master->max_queue_size);
+ }
+ else { // datagram does not fit in the current cycle
+#ifdef EC_HAVE_CYCLES
+ cycles_t cycles_now = get_cycles();
+
+ if (cycles_now - datagram->cycles_sent
+ > ext_injection_timeout_cycles)
+#else
+ if (jiffies - datagram->jiffies_sent
+ > ext_injection_timeout_jiffies)
+#endif
+ {
+#if defined EC_RT_SYSLOG || DEBUG_INJECT
+ unsigned int time_us;
+#endif
+
+ datagram->state = EC_DATAGRAM_ERROR;
+
+#if defined EC_RT_SYSLOG || DEBUG_INJECT
+#ifdef EC_HAVE_CYCLES
+ time_us = (unsigned int)
+ ((cycles_now - datagram->cycles_sent) * 1000LL)
+ / cpu_khz;
+#else
+ time_us = (unsigned int)
+ ((jiffies - datagram->jiffies_sent) * 1000000 / HZ);
+#endif
+ EC_MASTER_ERR(master, "Timeout %u us: Injecting"
+ " external datagram %s size=%zu,"
+ " max_queue_size=%zu\n", time_us, datagram->name,
+ datagram->data_size, master->max_queue_size);
+#endif
+ }
+ else {
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "Deferred injecting"
+ " external datagram %s size=%u, queue_size=%u\n",
+ datagram->name, datagram->data_size, queue_size);
+#endif
+ break;
+ }
+ }
+
+ master->ext_ring_idx_rt =
+ (master->ext_ring_idx_rt + 1) % EC_EXT_RING_SIZE;
+ }
+
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "Injected %u datagrams.\n", datagram_count);
+#endif
+}
+
+/*****************************************************************************/
+
+/** Sets the expected interval between calls to ecrt_master_send
+ * and calculates the maximum amount of data to queue.
+ */
+void ec_master_set_send_interval(
+ ec_master_t *master, /**< EtherCAT master */
+ unsigned int send_interval /**< Send interval */
+ )
+{
+ master->send_interval = send_interval;
+ master->max_queue_size =
+ (send_interval * 1000) / EC_BYTE_TRANSMISSION_TIME_NS;
+ master->max_queue_size -= master->max_queue_size / 10;
+}
+
+/*****************************************************************************/
+
+/** Searches for a free datagram in the external datagram ring.
+ *
+ * \return Next free datagram, or NULL.
+ */
+ec_datagram_t *ec_master_get_external_datagram(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ if ((master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE !=
+ master->ext_ring_idx_rt) {
+ ec_datagram_t *datagram =
+ &master->ext_datagram_ring[master->ext_ring_idx_fsm];
+ return datagram;
+ }
+ else {
+ return NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/** Places a datagram in the datagram queue.
+ */
+void ec_master_queue_datagram(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ ec_datagram_t *queued_datagram;
+
+ /* It is possible, that a datagram in the queue is re-initialized with the
+ * ec_datagram_<type>() methods and then shall be queued with this method.
+ * In that case, the state is already reset to EC_DATAGRAM_INIT. Check if
+ * the datagram is queued to avoid duplicate queuing (which results in an
+ * infinite loop!). Set the state to EC_DATAGRAM_QUEUED again, probably
+ * causing an unmatched datagram. */
+ list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
+ if (queued_datagram == datagram) {
+ datagram->skip_count++;
+#ifdef EC_RT_SYSLOG
+ EC_MASTER_DBG(master, 1,
+ "Datagram %p already queued (skipping).\n", datagram);
+#endif
+ datagram->state = EC_DATAGRAM_QUEUED;
+ return;
+ }
+ }
+
+ list_add_tail(&datagram->queue, &master->datagram_queue);
+ datagram->state = EC_DATAGRAM_QUEUED;
+}
+
+/*****************************************************************************/
+
+/** Places a datagram in the non-application datagram queue.
+ */
+void ec_master_queue_datagram_ext(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_datagram_t *datagram /**< datagram */
+ )
+{
+ down(&master->ext_queue_sem);
+ list_add_tail(&datagram->queue, &master->ext_datagram_queue);
+ up(&master->ext_queue_sem);
+}
+
+/*****************************************************************************/
+
+/** Sends the datagrams in the queue for a certain device.
+ *
+ */
+void ec_master_send_datagrams(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_device_index_t device_index /**< Device index. */
+ )
+{
+ ec_datagram_t *datagram, *next;
+ size_t datagram_size;
+ uint8_t *frame_data, *cur_data = NULL;
+ void *follows_word;
+#ifdef EC_HAVE_CYCLES
+ cycles_t cycles_start, cycles_sent, cycles_end;
+#endif
+ unsigned long jiffies_sent;
+ unsigned int frame_count, more_datagrams_waiting;
+ struct list_head sent_datagrams;
+
+#ifdef EC_HAVE_CYCLES
+ cycles_start = get_cycles();
+#endif
+ frame_count = 0;
+ INIT_LIST_HEAD(&sent_datagrams);
+
+ EC_MASTER_DBG(master, 2, "%s(device_index = %u)\n",
+ __func__, device_index);
+
+ do {
+ frame_data = NULL;
+ follows_word = NULL;
+ more_datagrams_waiting = 0;
+
+ // fill current frame with datagrams
+ list_for_each_entry(datagram, &master->datagram_queue, queue) {
+ if (datagram->state != EC_DATAGRAM_QUEUED ||
+ datagram->device_index != device_index) {
+ continue;
+ }
+
+ if (!frame_data) {
+ // fetch pointer to transmit socket buffer
+ frame_data =
+ ec_device_tx_data(&master->devices[device_index]);
+ cur_data = frame_data + EC_FRAME_HEADER_SIZE;
+ }
+
+ // does the current datagram fit in the frame?
+ datagram_size = EC_DATAGRAM_HEADER_SIZE + datagram->data_size
+ + EC_DATAGRAM_FOOTER_SIZE;
+ if (cur_data - frame_data + datagram_size > ETH_DATA_LEN) {
+ more_datagrams_waiting = 1;
+ break;
+ }
+
+ list_add_tail(&datagram->sent, &sent_datagrams);
+ datagram->index = master->datagram_index++;
+
+ EC_MASTER_DBG(master, 2, "Adding datagram 0x%02X\n",
+ datagram->index);
+
+ // set "datagram following" flag in previous datagram
+ if (follows_word) {
+ EC_WRITE_U16(follows_word,
+ EC_READ_U16(follows_word) | 0x8000);
+ }
+
+ // EtherCAT datagram header
+ EC_WRITE_U8 (cur_data, datagram->type);
+ EC_WRITE_U8 (cur_data + 1, datagram->index);
+ memcpy(cur_data + 2, datagram->address, EC_ADDR_LEN);
+ EC_WRITE_U16(cur_data + 6, datagram->data_size & 0x7FF);
+ EC_WRITE_U16(cur_data + 8, 0x0000);
+ follows_word = cur_data + 6;
+ cur_data += EC_DATAGRAM_HEADER_SIZE;
+
+ // EtherCAT datagram data
+ memcpy(cur_data, datagram->data, datagram->data_size);
+ cur_data += datagram->data_size;
+
+ // EtherCAT datagram footer
+ EC_WRITE_U16(cur_data, 0x0000); // reset working counter
+ cur_data += EC_DATAGRAM_FOOTER_SIZE;
+ }
+
+ if (list_empty(&sent_datagrams)) {
+ EC_MASTER_DBG(master, 2, "nothing to send.\n");
+ break;
+ }
+
+ // EtherCAT frame header
+ EC_WRITE_U16(frame_data, ((cur_data - frame_data
+ - EC_FRAME_HEADER_SIZE) & 0x7FF) | 0x1000);
+
+ // pad frame
+ while (cur_data - frame_data < ETH_ZLEN - ETH_HLEN)
+ EC_WRITE_U8(cur_data++, 0x00);
+
+ EC_MASTER_DBG(master, 2, "frame size: %zu\n", cur_data - frame_data);
+
+ // send frame
+ ec_device_send(&master->devices[device_index],
+ cur_data - frame_data);
+#ifdef EC_HAVE_CYCLES
+ cycles_sent = get_cycles();
+#endif
+ jiffies_sent = jiffies;
+
+ // set datagram states and sending timestamps
+ list_for_each_entry_safe(datagram, next, &sent_datagrams, sent) {
+ datagram->state = EC_DATAGRAM_SENT;
+#ifdef EC_HAVE_CYCLES
+ datagram->cycles_sent = cycles_sent;
+#endif
+ datagram->jiffies_sent = jiffies_sent;
+ list_del_init(&datagram->sent); // empty list of sent datagrams
+ }
+
+ frame_count++;
+ }
+ while (more_datagrams_waiting);
+
+#ifdef EC_HAVE_CYCLES
+ if (unlikely(master->debug_level > 1)) {
+ cycles_end = get_cycles();
+ EC_MASTER_DBG(master, 0, "%s()"
+ " sent %u frames in %uus.\n", __func__, frame_count,
+ (unsigned int) (cycles_end - cycles_start) * 1000 / cpu_khz);
+ }
+#endif
+}
+
+/*****************************************************************************/
+
+/** Processes a received frame.
+ *
+ * This function is called by the network driver for every received frame.
+ *
+ * \return 0 in case of success, else < 0
+ */
+void ec_master_receive_datagrams(
+ ec_master_t *master, /**< EtherCAT master */
+ ec_device_t *device, /**< EtherCAT device */
+ const uint8_t *frame_data, /**< frame data */
+ size_t size /**< size of the received data */
+ )
+{
+ size_t frame_size, data_size;
+ uint8_t datagram_type, datagram_index;
+ unsigned int cmd_follows, matched;
+ const uint8_t *cur_data;
+ ec_datagram_t *datagram;
+
+ if (unlikely(size < EC_FRAME_HEADER_SIZE)) {
+ if (master->debug_level || FORCE_OUTPUT_CORRUPTED) {
+ EC_MASTER_DBG(master, 0, "Corrupted frame received"
+ " on %s (size %zu < %u byte):\n",
+ device->dev->name, size, EC_FRAME_HEADER_SIZE);
+ ec_print_data(frame_data, size);
+ }
+ master->stats.corrupted++;
+#ifdef EC_RT_SYSLOG
+ ec_master_output_stats(master);
+#endif
+ return;
+ }
+
+ cur_data = frame_data;
+
+ // check length of entire frame
+ frame_size = EC_READ_U16(cur_data) & 0x07FF;
+ cur_data += EC_FRAME_HEADER_SIZE;
+
+ if (unlikely(frame_size > size)) {
+ if (master->debug_level || FORCE_OUTPUT_CORRUPTED) {
+ EC_MASTER_DBG(master, 0, "Corrupted frame received"
+ " on %s (invalid frame size %zu for "
+ "received size %zu):\n", device->dev->name,
+ frame_size, size);
+ ec_print_data(frame_data, size);
+ }
+ master->stats.corrupted++;
+#ifdef EC_RT_SYSLOG
+ ec_master_output_stats(master);
+#endif
+ return;
+ }
+
+ cmd_follows = 1;
+ while (cmd_follows) {
+ // process datagram header
+ datagram_type = EC_READ_U8 (cur_data);
+ datagram_index = EC_READ_U8 (cur_data + 1);
+ data_size = EC_READ_U16(cur_data + 6) & 0x07FF;
+ cmd_follows = EC_READ_U16(cur_data + 6) & 0x8000;
+ cur_data += EC_DATAGRAM_HEADER_SIZE;
+
+ if (unlikely(cur_data - frame_data
+ + data_size + EC_DATAGRAM_FOOTER_SIZE > size)) {
+ if (master->debug_level || FORCE_OUTPUT_CORRUPTED) {
+ EC_MASTER_DBG(master, 0, "Corrupted frame received"
+ " on %s (invalid data size %zu):\n",
+ device->dev->name, data_size);
+ ec_print_data(frame_data, size);
+ }
+ master->stats.corrupted++;
+#ifdef EC_RT_SYSLOG
+ ec_master_output_stats(master);
+#endif
+ return;
+ }
+
+ // search for matching datagram in the queue
+ matched = 0;
+ list_for_each_entry(datagram, &master->datagram_queue, queue) {
+ if (datagram->index == datagram_index
+ && datagram->state == EC_DATAGRAM_SENT
+ && datagram->type == datagram_type
+ && datagram->data_size == data_size) {
+ matched = 1;
+ break;
+ }
+ }
+
+ // no matching datagram was found
+ if (!matched) {
+ master->stats.unmatched++;
+#ifdef EC_RT_SYSLOG
+ ec_master_output_stats(master);
+#endif
+
+ if (unlikely(master->debug_level > 0)) {
+ EC_MASTER_DBG(master, 0, "UNMATCHED datagram:\n");
+ ec_print_data(cur_data - EC_DATAGRAM_HEADER_SIZE,
+ EC_DATAGRAM_HEADER_SIZE + data_size
+ + EC_DATAGRAM_FOOTER_SIZE);
+#ifdef EC_DEBUG_RING
+ ec_device_debug_ring_print(&master->devices[EC_DEVICE_MAIN]);
+#endif
+ }
+
+ cur_data += data_size + EC_DATAGRAM_FOOTER_SIZE;
+ continue;
+ }
+
+ if (datagram->type != EC_DATAGRAM_APWR &&
+ datagram->type != EC_DATAGRAM_FPWR &&
+ datagram->type != EC_DATAGRAM_BWR &&
+ datagram->type != EC_DATAGRAM_LWR) {
+ // copy received data into the datagram memory,
+ // if something has been read
+ memcpy(datagram->data, cur_data, data_size);
+ }
+ cur_data += data_size;
+
+ // set the datagram's working counter
+ datagram->working_counter = EC_READ_U16(cur_data);
+ cur_data += EC_DATAGRAM_FOOTER_SIZE;
+
+ // dequeue the received datagram
+ datagram->state = EC_DATAGRAM_RECEIVED;
+#ifdef EC_HAVE_CYCLES
+ datagram->cycles_received =
+ master->devices[EC_DEVICE_MAIN].cycles_poll;
+#endif
+ datagram->jiffies_received =
+ master->devices[EC_DEVICE_MAIN].jiffies_poll;
+ list_del_init(&datagram->queue);
+ }
+}
+
+/*****************************************************************************/
+
+/** Output master statistics.
+ *
+ * This function outputs statistical data on demand, but not more often than
+ * necessary. The output happens at most once a second.
+ */
+void ec_master_output_stats(ec_master_t *master /**< EtherCAT master */)
+{
+ if (unlikely(jiffies - master->stats.output_jiffies >= HZ)) {
+ master->stats.output_jiffies = jiffies;
+
+ if (master->stats.timeouts) {
+ EC_MASTER_WARN(master, "%u datagram%s TIMED OUT!\n",
+ master->stats.timeouts,
+ master->stats.timeouts == 1 ? "" : "s");
+ master->stats.timeouts = 0;
+ }
+ if (master->stats.corrupted) {
+ EC_MASTER_WARN(master, "%u frame%s CORRUPTED!\n",
+ master->stats.corrupted,
+ master->stats.corrupted == 1 ? "" : "s");
+ master->stats.corrupted = 0;
+ }
+ if (master->stats.unmatched) {
+ EC_MASTER_WARN(master, "%u datagram%s UNMATCHED!\n",
+ master->stats.unmatched,
+ master->stats.unmatched == 1 ? "" : "s");
+ master->stats.unmatched = 0;
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** Clears the common device statistics.
+ */
+void ec_master_clear_device_stats(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ unsigned int i;
+
+ // zero frame statistics
+ master->device_stats.tx_count = 0;
+ master->device_stats.last_tx_count = 0;
+ master->device_stats.rx_count = 0;
+ master->device_stats.last_rx_count = 0;
+ master->device_stats.tx_bytes = 0;
+ master->device_stats.last_tx_bytes = 0;
+ master->device_stats.rx_bytes = 0;
+ master->device_stats.last_rx_bytes = 0;
+ master->device_stats.last_loss = 0;
+
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ master->device_stats.tx_frame_rates[i] = 0;
+ master->device_stats.rx_frame_rates[i] = 0;
+ master->device_stats.tx_byte_rates[i] = 0;
+ master->device_stats.rx_byte_rates[i] = 0;
+ master->device_stats.loss_rates[i] = 0;
+ }
+
+ master->device_stats.jiffies = 0;
+}
+
+/*****************************************************************************/
+
+/** Updates the common device statistics.
+ */
+void ec_master_update_device_stats(
+ ec_master_t *master /**< EtherCAT master */
+ )
+{
+ ec_device_stats_t *s = &master->device_stats;
+ s32 tx_frame_rate, rx_frame_rate, tx_byte_rate, rx_byte_rate, loss_rate;
+ u64 loss;
+ unsigned int i, dev_idx;
+
+ // frame statistics
+ if (likely(jiffies - s->jiffies < HZ)) {
+ return;
+ }
+
+ tx_frame_rate = (s->tx_count - s->last_tx_count) * 1000;
+ rx_frame_rate = (s->rx_count - s->last_rx_count) * 1000;
+ tx_byte_rate = s->tx_bytes - s->last_tx_bytes;
+ rx_byte_rate = s->rx_bytes - s->last_rx_bytes;
+ loss = s->tx_count - s->rx_count;
+ loss_rate = (loss - s->last_loss) * 1000;
+
+ /* Low-pass filter:
+ * Y_n = y_(n - 1) + T / tau * (x - y_(n - 1)) | T = 1
+ * -> Y_n += (x - y_(n - 1)) / tau
+ */
+ for (i = 0; i < EC_RATE_COUNT; i++) {
+ s32 n = rate_intervals[i];
+ s->tx_frame_rates[i] += (tx_frame_rate - s->tx_frame_rates[i]) / n;
+ s->rx_frame_rates[i] += (rx_frame_rate - s->rx_frame_rates[i]) / n;
+ s->tx_byte_rates[i] += (tx_byte_rate - s->tx_byte_rates[i]) / n;
+ s->rx_byte_rates[i] += (rx_byte_rate - s->rx_byte_rates[i]) / n;
+ s->loss_rates[i] += (loss_rate - s->loss_rates[i]) / n;
+ }
+
+ s->last_tx_count = s->tx_count;
+ s->last_rx_count = s->rx_count;
+ s->last_tx_bytes = s->tx_bytes;
+ s->last_rx_bytes = s->rx_bytes;
+ s->last_loss = loss;
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ ec_device_update_stats(&master->devices[dev_idx]);
+ }
+
+ s->jiffies = jiffies;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_USE_HRTIMER
+
+/*
+ * Sleep related functions:
+ */
+static enum hrtimer_restart ec_master_nanosleep_wakeup(struct hrtimer *timer)
+{
+ struct hrtimer_sleeper *t =
+ container_of(timer, struct hrtimer_sleeper, timer);
+ struct task_struct *task = t->task;
+
+ t->task = NULL;
+ if (task)
+ wake_up_process(task);
+
+ return HRTIMER_NORESTART;
+}
+
+/*****************************************************************************/
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,28)
+
+/* compatibility with new hrtimer interface */
+static inline ktime_t hrtimer_get_expires(const struct hrtimer *timer)
+{
+ return timer->expires;
+}
+
+/*****************************************************************************/
+
+static inline void hrtimer_set_expires(struct hrtimer *timer, ktime_t time)
+{
+ timer->expires = time;
+}
+
+#endif
+
+/*****************************************************************************/
+
+void ec_master_nanosleep(const unsigned long nsecs)
+{
+ struct hrtimer_sleeper t;
+ enum hrtimer_mode mode = HRTIMER_MODE_REL;
+
+ hrtimer_init(&t.timer, CLOCK_MONOTONIC, mode);
+ t.timer.function = ec_master_nanosleep_wakeup;
+ t.task = current;
+#ifdef CONFIG_HIGH_RES_TIMERS
+#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 24)
+ t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_RESTART;
+#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 26)
+ t.timer.cb_mode = HRTIMER_CB_IRQSAFE_NO_SOFTIRQ;
+#elif LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 28)
+ t.timer.cb_mode = HRTIMER_CB_IRQSAFE_UNLOCKED;
+#endif
+#endif
+ hrtimer_set_expires(&t.timer, ktime_set(0, nsecs));
+
+ do {
+ set_current_state(TASK_INTERRUPTIBLE);
+ hrtimer_start(&t.timer, hrtimer_get_expires(&t.timer), mode);
+
+ if (likely(t.task))
+ schedule();
+
+ hrtimer_cancel(&t.timer);
+ mode = HRTIMER_MODE_ABS;
+
+ } while (t.task && !signal_pending(current));
+}
+
+#endif // EC_USE_HRTIMER
+
+/*****************************************************************************/
+
+/* compatibility for priority changes */
+static inline void set_normal_priority(struct task_struct *p, int nice)
+{
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
+ sched_set_normal(p, nice);
+#else
+ struct sched_param param = { .sched_priority = 0 };
+ sched_setscheduler(p, SCHED_NORMAL, &param);
+ set_user_nice(p, nice);
+#endif
+}
+
+/*****************************************************************************/
+
+/** Execute slave FSMs.
+ */
+void ec_master_exec_slave_fsms(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ ec_datagram_t *datagram;
+ ec_fsm_slave_t *fsm, *next;
+ unsigned int count = 0;
+
+ list_for_each_entry_safe(fsm, next, &master->fsm_exec_list, list) {
+ if (!fsm->datagram) {
+ EC_MASTER_WARN(master, "Slave %u FSM has zero datagram."
+ "This is a bug!\n", fsm->slave->ring_position);
+ list_del_init(&fsm->list);
+ master->fsm_exec_count--;
+ return;
+ }
+
+ if (fsm->datagram->state == EC_DATAGRAM_INIT ||
+ fsm->datagram->state == EC_DATAGRAM_QUEUED ||
+ fsm->datagram->state == EC_DATAGRAM_SENT) {
+ // previous datagram was not sent or received yet.
+ // wait until next thread execution
+ return;
+ }
+
+ datagram = ec_master_get_external_datagram(master);
+ if (!datagram) {
+ // no free datagrams at the moment
+ EC_MASTER_WARN(master, "No free datagram during"
+ " slave FSM execution. This is a bug!\n");
+ continue;
+ }
+
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "Executing slave %u FSM.\n",
+ fsm->slave->ring_position);
+#endif
+ if (ec_fsm_slave_exec(fsm, datagram)) {
+ // FSM consumed datagram
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "FSM consumed datagram %s\n",
+ datagram->name);
+#endif
+ master->ext_ring_idx_fsm =
+ (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE;
+ }
+ else {
+ // FSM finished
+ list_del_init(&fsm->list);
+ master->fsm_exec_count--;
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "FSM finished. %u remaining.\n",
+ master->fsm_exec_count);
+#endif
+ }
+ }
+
+ while (master->fsm_exec_count < EC_EXT_RING_SIZE / 2
+ && count < master->slave_count) {
+
+ if (ec_fsm_slave_is_ready(&master->fsm_slave->fsm)) {
+ datagram = ec_master_get_external_datagram(master);
+
+ if (ec_fsm_slave_exec(&master->fsm_slave->fsm, datagram)) {
+ master->ext_ring_idx_fsm =
+ (master->ext_ring_idx_fsm + 1) % EC_EXT_RING_SIZE;
+ list_add_tail(&master->fsm_slave->fsm.list,
+ &master->fsm_exec_list);
+ master->fsm_exec_count++;
+#if DEBUG_INJECT
+ EC_MASTER_DBG(master, 1, "New slave %u FSM"
+ " consumed datagram %s, now %u FSMs in list.\n",
+ master->fsm_slave->ring_position, datagram->name,
+ master->fsm_exec_count);
+#endif
+ }
+ }
+
+ master->fsm_slave++;
+ if (master->fsm_slave >= master->slaves + master->slave_count) {
+ master->fsm_slave = master->slaves;
+ }
+ count++;
+ }
+}
+
+/*****************************************************************************/
+
+/** Master kernel thread function for IDLE phase.
+ */
+static int ec_master_idle_thread(void *priv_data)
+{
+ ec_master_t *master = (ec_master_t *) priv_data;
+ int fsm_exec;
+#ifdef EC_USE_HRTIMER
+ size_t sent_bytes;
+#endif
+
+ // send interval in IDLE phase
+ ec_master_set_send_interval(master, 1000000 / HZ);
+
+ EC_MASTER_DBG(master, 1, "Idle thread running with send interval = %u us,"
+ " max data size=%zu\n", master->send_interval,
+ master->max_queue_size);
+
+ while (!kthread_should_stop()) {
+ ec_datagram_output_stats(&master->fsm_datagram);
+
+ // receive
+ down(&master->io_sem);
+ ecrt_master_receive(master);
+ up(&master->io_sem);
+
+ // execute master & slave state machines
+ if (down_interruptible(&master->master_sem)) {
+ break;
+ }
+
+ fsm_exec = ec_fsm_master_exec(&master->fsm);
+
+ ec_master_exec_slave_fsms(master);
+
+ up(&master->master_sem);
+
+ // queue and send
+ down(&master->io_sem);
+ if (fsm_exec) {
+ ec_master_queue_datagram(master, &master->fsm_datagram);
+ }
+ ecrt_master_send(master);
+#ifdef EC_USE_HRTIMER
+ sent_bytes = master->devices[EC_DEVICE_MAIN].tx_skb[
+ master->devices[EC_DEVICE_MAIN].tx_ring_index]->len;
+#endif
+ up(&master->io_sem);
+
+ if (ec_fsm_master_idle(&master->fsm)) {
+#ifdef EC_USE_HRTIMER
+ ec_master_nanosleep(master->send_interval * 1000);
+#else
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+#endif
+ } else {
+#ifdef EC_USE_HRTIMER
+ ec_master_nanosleep(sent_bytes * EC_BYTE_TRANSMISSION_TIME_NS);
+#else
+ schedule();
+#endif
+ }
+ }
+
+ EC_MASTER_DBG(master, 1, "Master IDLE thread exiting...\n");
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Master kernel thread function for OPERATION phase.
+ */
+static int ec_master_operation_thread(void *priv_data)
+{
+ ec_master_t *master = (ec_master_t *) priv_data;
+
+ EC_MASTER_DBG(master, 1, "Operation thread running"
+ " with fsm interval = %u us, max data size=%zu\n",
+ master->send_interval, master->max_queue_size);
+
+ while (!kthread_should_stop()) {
+ ec_datagram_output_stats(&master->fsm_datagram);
+
+ if (master->injection_seq_rt == master->injection_seq_fsm) {
+ // output statistics
+ ec_master_output_stats(master);
+
+ // execute master & slave state machines
+ if (down_interruptible(&master->master_sem)) {
+ break;
+ }
+
+ if (ec_fsm_master_exec(&master->fsm)) {
+ // Inject datagrams (let the RT thread queue them, see
+ // ecrt_master_send())
+ master->injection_seq_fsm++;
+ }
+
+ ec_master_exec_slave_fsms(master);
+
+ up(&master->master_sem);
+ }
+
+#ifdef EC_USE_HRTIMER
+ // the op thread should not work faster than the sending RT thread
+ ec_master_nanosleep(master->send_interval * 1000);
+#else
+ if (ec_fsm_master_idle(&master->fsm)) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ }
+ else {
+ schedule();
+ }
+#endif
+ }
+
+ EC_MASTER_DBG(master, 1, "Master OP thread exiting...\n");
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+/** Starts Ethernet over EtherCAT processing on demand.
+ */
+void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
+{
+ if (master->eoe_thread) {
+ EC_MASTER_WARN(master, "EoE already running!\n");
+ return;
+ }
+
+ if (list_empty(&master->eoe_handlers))
+ return;
+
+ if (!master->send_cb || !master->receive_cb) {
+ EC_MASTER_WARN(master, "No EoE processing"
+ " because of missing callbacks!\n");
+ return;
+ }
+
+ EC_MASTER_INFO(master, "Starting EoE thread.\n");
+ master->eoe_thread = kthread_run(ec_master_eoe_thread, master,
+ "EtherCAT-EoE");
+ if (IS_ERR(master->eoe_thread)) {
+ int err = (int) PTR_ERR(master->eoe_thread);
+ EC_MASTER_ERR(master, "Failed to start EoE thread (error %i)!\n",
+ err);
+ master->eoe_thread = NULL;
+ return;
+ }
+
+ set_normal_priority(master->eoe_thread, 0);
+}
+
+/*****************************************************************************/
+
+/** Stops the Ethernet over EtherCAT processing.
+ */
+void ec_master_eoe_stop(ec_master_t *master /**< EtherCAT master */)
+{
+ if (master->eoe_thread) {
+ EC_MASTER_INFO(master, "Stopping EoE thread.\n");
+
+ kthread_stop(master->eoe_thread);
+ master->eoe_thread = NULL;
+ EC_MASTER_INFO(master, "EoE thread exited.\n");
+ }
+}
+
+/*****************************************************************************/
+
+/** Does the Ethernet over EtherCAT processing.
+ */
+static int ec_master_eoe_thread(void *priv_data)
+{
+ ec_master_t *master = (ec_master_t *) priv_data;
+ ec_eoe_t *eoe;
+ unsigned int none_open, sth_to_send, all_idle;
+
+ EC_MASTER_DBG(master, 1, "EoE thread running.\n");
+
+ while (!kthread_should_stop()) {
+ none_open = 1;
+ all_idle = 1;
+
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ if (ec_eoe_is_open(eoe)) {
+ none_open = 0;
+ break;
+ }
+ }
+ if (none_open)
+ goto schedule;
+
+ // receive datagrams
+ master->receive_cb(master->cb_data);
+
+ // actual EoE processing
+ sth_to_send = 0;
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ ec_eoe_run(eoe);
+ if (eoe->queue_datagram) {
+ sth_to_send = 1;
+ }
+ if (!ec_eoe_is_idle(eoe)) {
+ all_idle = 0;
+ }
+ }
+
+ if (sth_to_send) {
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ ec_eoe_queue(eoe);
+ }
+ // (try to) send datagrams
+ down(&master->ext_queue_sem);
+ master->send_cb(master->cb_data);
+ up(&master->ext_queue_sem);
+ }
+
+schedule:
+ if (all_idle) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ schedule_timeout(1);
+ } else {
+ schedule();
+ }
+ }
+
+ EC_MASTER_DBG(master, 1, "EoE thread exiting...\n");
+ return 0;
+}
+#endif
+
+/*****************************************************************************/
+
+/** Attaches the slave configurations to the slaves.
+ */
+void ec_master_attach_slave_configs(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ ec_slave_config_t *sc;
+
+ list_for_each_entry(sc, &master->configs, list) {
+ ec_slave_config_attach(sc);
+ }
+}
+
+/*****************************************************************************/
+
+/** Common implementation for ec_master_find_slave()
+ * and ec_master_find_slave_const().
+ */
+#define EC_FIND_SLAVE \
+ do { \
+ if (alias) { \
+ for (; slave < master->slaves + master->slave_count; \
+ slave++) { \
+ if (slave->effective_alias == alias) \
+ break; \
+ } \
+ if (slave == master->slaves + master->slave_count) \
+ return NULL; \
+ } \
+ \
+ slave += position; \
+ if (slave < master->slaves + master->slave_count) { \
+ return slave; \
+ } else { \
+ return NULL; \
+ } \
+ } while (0)
+
+/** Finds a slave in the bus, given the alias and position.
+ *
+ * \return Search result, or NULL.
+ */
+ec_slave_t *ec_master_find_slave(
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t alias, /**< Slave alias. */
+ uint16_t position /**< Slave position. */
+ )
+{
+ ec_slave_t *slave = master->slaves;
+ EC_FIND_SLAVE;
+}
+
+/** Finds a slave in the bus, given the alias and position.
+ *
+ * Const version.
+ *
+ * \return Search result, or NULL.
+ */
+const ec_slave_t *ec_master_find_slave_const(
+ const ec_master_t *master, /**< EtherCAT master. */
+ uint16_t alias, /**< Slave alias. */
+ uint16_t position /**< Slave position. */
+ )
+{
+ const ec_slave_t *slave = master->slaves;
+ EC_FIND_SLAVE;
+}
+
+/*****************************************************************************/
+
+/** Get the number of slave configurations provided by the application.
+ *
+ * \return Number of configurations.
+ */
+unsigned int ec_master_config_count(
+ const ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ const ec_slave_config_t *sc;
+ unsigned int count = 0;
+
+ list_for_each_entry(sc, &master->configs, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Common implementation for ec_master_get_config()
+ * and ec_master_get_config_const().
+ */
+#define EC_FIND_CONFIG \
+ do { \
+ list_for_each_entry(sc, &master->configs, list) { \
+ if (pos--) \
+ continue; \
+ return sc; \
+ } \
+ return NULL; \
+ } while (0)
+
+/** Get a slave configuration via its position in the list.
+ *
+ * \return Slave configuration or \a NULL.
+ */
+ec_slave_config_t *ec_master_get_config(
+ const ec_master_t *master, /**< EtherCAT master. */
+ unsigned int pos /**< List position. */
+ )
+{
+ ec_slave_config_t *sc;
+ EC_FIND_CONFIG;
+}
+
+/** Get a slave configuration via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Slave configuration or \a NULL.
+ */
+const ec_slave_config_t *ec_master_get_config_const(
+ const ec_master_t *master, /**< EtherCAT master. */
+ unsigned int pos /**< List position. */
+ )
+{
+ const ec_slave_config_t *sc;
+ EC_FIND_CONFIG;
+}
+
+/*****************************************************************************/
+
+/** Get the number of domains.
+ *
+ * \return Number of domains.
+ */
+unsigned int ec_master_domain_count(
+ const ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ const ec_domain_t *domain;
+ unsigned int count = 0;
+
+ list_for_each_entry(domain, &master->domains, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Common implementation for ec_master_find_domain() and
+ * ec_master_find_domain_const().
+ */
+#define EC_FIND_DOMAIN \
+ do { \
+ list_for_each_entry(domain, &master->domains, list) { \
+ if (index--) \
+ continue; \
+ return domain; \
+ } \
+ \
+ return NULL; \
+ } while (0)
+
+/** Get a domain via its position in the list.
+ *
+ * \return Domain pointer, or \a NULL if not found.
+ */
+ec_domain_t *ec_master_find_domain(
+ ec_master_t *master, /**< EtherCAT master. */
+ unsigned int index /**< Domain index. */
+ )
+{
+ ec_domain_t *domain;
+ EC_FIND_DOMAIN;
+}
+
+/** Get a domain via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Domain pointer, or \a NULL if not found.
+ */
+const ec_domain_t *ec_master_find_domain_const(
+ const ec_master_t *master, /**< EtherCAT master. */
+ unsigned int index /**< Domain index. */
+ )
+{
+ const ec_domain_t *domain;
+ EC_FIND_DOMAIN;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+
+/** Get the number of EoE handlers.
+ *
+ * \return Number of EoE handlers.
+ */
+uint16_t ec_master_eoe_handler_count(
+ const ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ const ec_eoe_t *eoe;
+ unsigned int count = 0;
+
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Get an EoE handler via its position in the list.
+ *
+ * Const version.
+ *
+ * \return EoE handler pointer, or \a NULL if not found.
+ */
+const ec_eoe_t *ec_master_get_eoe_handler_const(
+ const ec_master_t *master, /**< EtherCAT master. */
+ uint16_t index /**< EoE handler index. */
+ )
+{
+ const ec_eoe_t *eoe;
+
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ if (index--)
+ continue;
+ return eoe;
+ }
+
+ return NULL;
+}
+
+#endif
+
+/*****************************************************************************/
+
+/** Set the debug level.
+ *
+ * \retval 0 Success.
+ * \retval -EINVAL Invalid debug level.
+ */
+int ec_master_debug_level(
+ ec_master_t *master, /**< EtherCAT master. */
+ unsigned int level /**< Debug level. May be 0, 1 or 2. */
+ )
+{
+ if (level > 2) {
+ EC_MASTER_ERR(master, "Invalid debug level %u!\n", level);
+ return -EINVAL;
+ }
+
+ if (level != master->debug_level) {
+ master->debug_level = level;
+ EC_MASTER_INFO(master, "Master debug level set to %u.\n",
+ master->debug_level);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Finds the DC reference clock.
+ */
+void ec_master_find_dc_ref_clock(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ ec_slave_t *slave, *ref = NULL;
+
+ if (master->dc_ref_config) {
+ // Use application-selected reference clock
+ slave = master->dc_ref_config->slave;
+
+ if (slave) {
+ if (slave->base_dc_supported && slave->has_dc_system_time) {
+ ref = slave;
+ }
+ else {
+ EC_MASTER_WARN(master, "Slave %u can not act as a"
+ " DC reference clock!", slave->ring_position);
+ }
+ }
+ else {
+ EC_MASTER_WARN(master, "DC reference clock config (%u-%u)"
+ " has no slave attached!\n", master->dc_ref_config->alias,
+ master->dc_ref_config->position);
+ }
+ }
+ else {
+ // Use first slave with DC support as reference clock
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ if (slave->base_dc_supported && slave->has_dc_system_time) {
+ ref = slave;
+ break;
+ }
+ }
+
+ }
+
+ master->dc_ref_clock = ref;
+
+ if (ref) {
+ EC_MASTER_INFO(master, "Using slave %u as DC reference clock.\n",
+ ref->ring_position);
+ }
+ else {
+ EC_MASTER_INFO(master, "No DC reference clock found.\n");
+ }
+
+ // These calls always succeed, because the
+ // datagrams have been pre-allocated.
+ ec_datagram_fpwr(&master->ref_sync_datagram,
+ ref ? ref->station_address : 0xffff, 0x0910, 4);
+ ec_datagram_frmw(&master->sync_datagram,
+ ref ? ref->station_address : 0xffff, 0x0910, 4);
+}
+
+/*****************************************************************************/
+
+/** Calculates the bus topology; recursion function.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_master_calc_topology_rec(
+ ec_master_t *master, /**< EtherCAT master. */
+ ec_slave_t *port0_slave, /**< Slave at port 0. */
+ unsigned int *slave_position /**< Slave position. */
+ )
+{
+ ec_slave_t *slave = master->slaves + *slave_position;
+ unsigned int port_index;
+ int ret;
+
+ static const unsigned int next_table[EC_MAX_PORTS] = {
+ 3, 2, 0, 1
+ };
+
+ slave->ports[0].next_slave = port0_slave;
+
+ port_index = 3;
+ while (port_index != 0) {
+ if (!slave->ports[port_index].link.loop_closed) {
+ *slave_position = *slave_position + 1;
+ if (*slave_position < master->slave_count) {
+ slave->ports[port_index].next_slave =
+ master->slaves + *slave_position;
+ ret = ec_master_calc_topology_rec(master,
+ slave, slave_position);
+ if (ret) {
+ return ret;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ port_index = next_table[port_index];
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Calculates the bus topology.
+ */
+void ec_master_calc_topology(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ unsigned int slave_position = 0;
+
+ if (master->slave_count == 0)
+ return;
+
+ if (ec_master_calc_topology_rec(master, NULL, &slave_position))
+ EC_MASTER_ERR(master, "Failed to calculate bus topology.\n");
+}
+
+/*****************************************************************************/
+
+/** Calculates the bus transmission delays.
+ */
+void ec_master_calc_transmission_delays(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ ec_slave_t *slave;
+
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+ ec_slave_calc_port_delays(slave);
+ }
+
+ if (master->dc_ref_clock) {
+ uint32_t delay = 0;
+ ec_slave_calc_transmission_delays_rec(master->dc_ref_clock, &delay);
+ }
+}
+
+/*****************************************************************************/
+
+/** Distributed-clocks calculations.
+ */
+void ec_master_calc_dc(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ // find DC reference clock
+ ec_master_find_dc_ref_clock(master);
+
+ // calculate bus topology
+ ec_master_calc_topology(master);
+
+ ec_master_calc_transmission_delays(master);
+}
+
+/*****************************************************************************/
+
+/** Request OP state for configured slaves.
+ */
+void ec_master_request_op(
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ unsigned int i;
+ ec_slave_t *slave;
+
+ if (!master->active)
+ return;
+
+ EC_MASTER_DBG(master, 1, "Requesting OP...\n");
+
+ // request OP for all configured slaves
+ for (i = 0; i < master->slave_count; i++) {
+ slave = master->slaves + i;
+ if (slave->config) {
+ ec_slave_request_state(slave, EC_SLAVE_STATE_OP);
+ }
+ }
+
+ // always set DC reference clock to OP
+ if (master->dc_ref_clock) {
+ ec_slave_request_state(master->dc_ref_clock, EC_SLAVE_STATE_OP);
+ }
+}
+
+/******************************************************************************
+ * Application interface
+ *****************************************************************************/
+
+/** Same as ecrt_master_create_domain(), but with ERR_PTR() return value.
+ *
+ * \return New domain, or ERR_PTR() return value.
+ */
+ec_domain_t *ecrt_master_create_domain_err(
+ ec_master_t *master /**< master */
+ )
+{
+ ec_domain_t *domain, *last_domain;
+ unsigned int index;
+
+ EC_MASTER_DBG(master, 1, "ecrt_master_create_domain(master = 0x%p)\n",
+ master);
+
+ if (!(domain =
+ (ec_domain_t *) kmalloc(sizeof(ec_domain_t), GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Error allocating domain memory!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ down(&master->master_sem);
+
+ if (list_empty(&master->domains)) {
+ index = 0;
+ } else {
+ last_domain = list_entry(master->domains.prev, ec_domain_t, list);
+ index = last_domain->index + 1;
+ }
+
+ ec_domain_init(domain, master, index);
+ list_add_tail(&domain->list, &master->domains);
+
+ up(&master->master_sem);
+
+ EC_MASTER_DBG(master, 1, "Created domain %u.\n", domain->index);
+
+ return domain;
+}
+
+/*****************************************************************************/
+
+ec_domain_t *ecrt_master_create_domain(
+ ec_master_t *master /**< master */
+ )
+{
+ ec_domain_t *d = ecrt_master_create_domain_err(master);
+ return IS_ERR(d) ? NULL : d;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_activate(ec_master_t *master)
+{
+ uint32_t domain_offset;
+ ec_domain_t *domain;
+ int ret;
+#ifdef EC_EOE
+ int eoe_was_running;
+#endif
+
+ EC_MASTER_DBG(master, 1, "ecrt_master_activate(master = 0x%p)\n", master);
+
+ if (master->active) {
+ EC_MASTER_WARN(master, "%s: Master already active!\n", __func__);
+ return 0;
+ }
+
+ down(&master->master_sem);
+
+ // finish all domains
+ domain_offset = 0;
+ list_for_each_entry(domain, &master->domains, list) {
+ ret = ec_domain_finish(domain, domain_offset);
+ if (ret < 0) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Failed to finish domain 0x%p!\n", domain);
+ return ret;
+ }
+ domain_offset += domain->data_size;
+ }
+
+ up(&master->master_sem);
+
+ // restart EoE process and master thread with new locking
+
+ ec_master_thread_stop(master);
+#ifdef EC_EOE
+ eoe_was_running = master->eoe_thread != NULL;
+ ec_master_eoe_stop(master);
+#endif
+
+ EC_MASTER_DBG(master, 1, "FSM datagram is %p.\n", &master->fsm_datagram);
+
+ master->injection_seq_fsm = 0;
+ master->injection_seq_rt = 0;
+
+ master->send_cb = master->app_send_cb;
+ master->receive_cb = master->app_receive_cb;
+ master->cb_data = master->app_cb_data;
+
+#ifdef EC_EOE
+ if (eoe_was_running) {
+ ec_master_eoe_start(master);
+ }
+#endif
+ ret = ec_master_thread_start(master, ec_master_operation_thread,
+ "EtherCAT-OP");
+ if (ret < 0) {
+ EC_MASTER_ERR(master, "Failed to start master thread!\n");
+ return ret;
+ }
+
+ /* Allow scanning after a topology change. */
+ master->allow_scan = 1;
+
+ master->active = 1;
+
+ // notify state machine, that the configuration shall now be applied
+ master->config_changed = 1;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_deactivate(ec_master_t *master)
+{
+ ec_slave_t *slave;
+#ifdef EC_EOE
+ ec_eoe_t *eoe;
+ int eoe_was_running;
+#endif
+
+ EC_MASTER_DBG(master, 1, "%s(master = 0x%p)\n", __func__, master);
+
+ if (!master->active) {
+ EC_MASTER_WARN(master, "%s: Master not active.\n", __func__);
+ return;
+ }
+
+ ec_master_thread_stop(master);
+#ifdef EC_EOE
+ eoe_was_running = master->eoe_thread != NULL;
+ ec_master_eoe_stop(master);
+#endif
+
+ master->send_cb = ec_master_internal_send_cb;
+ master->receive_cb = ec_master_internal_receive_cb;
+ master->cb_data = master;
+
+ ec_master_clear_config(master);
+
+ for (slave = master->slaves;
+ slave < master->slaves + master->slave_count;
+ slave++) {
+
+ // set states for all slaves
+ ec_slave_request_state(slave, EC_SLAVE_STATE_PREOP);
+
+ // mark for reconfiguration, because the master could have no
+ // possibility for a reconfiguration between two sequential operation
+ // phases.
+ slave->force_config = 1;
+ }
+
+#ifdef EC_EOE
+ // ... but leave EoE slaves in OP
+ list_for_each_entry(eoe, &master->eoe_handlers, list) {
+ if (ec_eoe_is_open(eoe))
+ ec_slave_request_state(eoe->slave, EC_SLAVE_STATE_OP);
+ }
+#endif
+
+ master->app_time = 0ULL;
+ master->dc_ref_time = 0ULL;
+
+#ifdef EC_EOE
+ if (eoe_was_running) {
+ ec_master_eoe_start(master);
+ }
+#endif
+ if (ec_master_thread_start(master, ec_master_idle_thread,
+ "EtherCAT-IDLE")) {
+ EC_MASTER_WARN(master, "Failed to restart master thread!\n");
+ }
+
+ /* Disallow scanning to get into the same state like after a master
+ * request (after ec_master_enter_operation_phase() is called). */
+ master->allow_scan = 0;
+
+ master->active = 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_send(ec_master_t *master)
+{
+ ec_datagram_t *datagram, *n;
+ ec_device_index_t dev_idx;
+
+ if (master->injection_seq_rt != master->injection_seq_fsm) {
+ // inject datagram produced by master FSM
+ ec_master_queue_datagram(master, &master->fsm_datagram);
+ master->injection_seq_rt = master->injection_seq_fsm;
+ }
+
+ ec_master_inject_external_datagrams(master);
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ if (unlikely(!master->devices[dev_idx].link_state)) {
+ // link is down, no datagram can be sent
+ list_for_each_entry_safe(datagram, n,
+ &master->datagram_queue, queue) {
+ if (datagram->device_index == dev_idx) {
+ datagram->state = EC_DATAGRAM_ERROR;
+ list_del_init(&datagram->queue);
+ }
+ }
+
+ if (!master->devices[dev_idx].dev) {
+ continue;
+ }
+
+ // query link state
+ ec_device_poll(&master->devices[dev_idx]);
+
+ // clear frame statistics
+ ec_device_clear_stats(&master->devices[dev_idx]);
+ continue;
+ }
+
+ // send frames
+ ec_master_send_datagrams(master, dev_idx);
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_master_receive(ec_master_t *master)
+{
+ unsigned int dev_idx;
+ ec_datagram_t *datagram, *next;
+
+ // receive datagrams
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ ec_device_poll(&master->devices[dev_idx]);
+ }
+ ec_master_update_device_stats(master);
+
+ // dequeue all datagrams that timed out
+ list_for_each_entry_safe(datagram, next, &master->datagram_queue, queue) {
+ if (datagram->state != EC_DATAGRAM_SENT) continue;
+
+#ifdef EC_HAVE_CYCLES
+ if (master->devices[EC_DEVICE_MAIN].cycles_poll -
+ datagram->cycles_sent > timeout_cycles) {
+#else
+ if (master->devices[EC_DEVICE_MAIN].jiffies_poll -
+ datagram->jiffies_sent > timeout_jiffies) {
+#endif
+ list_del_init(&datagram->queue);
+ datagram->state = EC_DATAGRAM_TIMED_OUT;
+ master->stats.timeouts++;
+
+#ifdef EC_RT_SYSLOG
+ ec_master_output_stats(master);
+
+ if (unlikely(master->debug_level > 0)) {
+ unsigned int time_us;
+#ifdef EC_HAVE_CYCLES
+ time_us = (unsigned int)
+ (master->devices[EC_DEVICE_MAIN].cycles_poll -
+ datagram->cycles_sent) * 1000 / cpu_khz;
+#else
+ time_us = (unsigned int)
+ ((master->devices[EC_DEVICE_MAIN].jiffies_poll -
+ datagram->jiffies_sent) * 1000000 / HZ);
+#endif
+ EC_MASTER_DBG(master, 0, "TIMED OUT datagram %p,"
+ " index %02X waited %u us.\n",
+ datagram, datagram->index, time_us);
+ }
+#endif /* RT_SYSLOG */
+ }
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_master_send_ext(ec_master_t *master)
+{
+ ec_datagram_t *datagram, *next;
+
+ list_for_each_entry_safe(datagram, next, &master->ext_datagram_queue,
+ queue) {
+ list_del(&datagram->queue);
+ ec_master_queue_datagram(master, datagram);
+ }
+
+ ecrt_master_send(master);
+}
+
+/*****************************************************************************/
+
+/** Same as ecrt_master_slave_config(), but with ERR_PTR() return value.
+ */
+ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *master,
+ uint16_t alias, uint16_t position, uint32_t vendor_id,
+ uint32_t product_code)
+{
+ ec_slave_config_t *sc;
+ unsigned int found = 0;
+
+
+ EC_MASTER_DBG(master, 1, "ecrt_master_slave_config(master = 0x%p,"
+ " alias = %u, position = %u, vendor_id = 0x%08x,"
+ " product_code = 0x%08x)\n",
+ master, alias, position, vendor_id, product_code);
+
+ list_for_each_entry(sc, &master->configs, list) {
+ if (sc->alias == alias && sc->position == position) {
+ found = 1;
+ break;
+ }
+ }
+
+ if (found) { // config with same alias/position already existing
+ if (sc->vendor_id != vendor_id || sc->product_code != product_code) {
+ EC_MASTER_ERR(master, "Slave type mismatch. Slave was"
+ " configured as 0x%08X/0x%08X before. Now configuring"
+ " with 0x%08X/0x%08X.\n", sc->vendor_id, sc->product_code,
+ vendor_id, product_code);
+ return ERR_PTR(-ENOENT);
+ }
+ } else {
+ EC_MASTER_DBG(master, 1, "Creating slave configuration for %u:%u,"
+ " 0x%08X/0x%08X.\n",
+ alias, position, vendor_id, product_code);
+
+ if (!(sc = (ec_slave_config_t *) kmalloc(sizeof(ec_slave_config_t),
+ GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate memory"
+ " for slave configuration.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ec_slave_config_init(sc, master,
+ alias, position, vendor_id, product_code);
+
+ down(&master->master_sem);
+
+ // try to find the addressed slave
+ ec_slave_config_attach(sc);
+ ec_slave_config_load_default_sync_config(sc);
+ list_add_tail(&sc->list, &master->configs);
+
+ up(&master->master_sem);
+ }
+
+ return sc;
+}
+
+/*****************************************************************************/
+
+ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
+ uint16_t alias, uint16_t position, uint32_t vendor_id,
+ uint32_t product_code)
+{
+ ec_slave_config_t *sc = ecrt_master_slave_config_err(master, alias,
+ position, vendor_id, product_code);
+ return IS_ERR(sc) ? NULL : sc;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_select_reference_clock(ec_master_t *master,
+ ec_slave_config_t *sc)
+{
+ if (sc) {
+ ec_slave_t *slave = sc->slave;
+
+ // output an early warning
+ if (slave &&
+ (!slave->base_dc_supported || !slave->has_dc_system_time)) {
+ EC_MASTER_WARN(master, "Slave %u can not act as"
+ " a reference clock!", slave->ring_position);
+ }
+ }
+
+ master->dc_ref_config = sc;
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
+{
+ EC_MASTER_DBG(master, 1, "ecrt_master(master = 0x%p,"
+ " master_info = 0x%p)\n", master, master_info);
+
+ master_info->slave_count = master->slave_count;
+ master_info->link_up = master->devices[EC_DEVICE_MAIN].link_state;
+ master_info->scan_busy = master->scan_busy;
+ master_info->app_time = master->app_time;
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_get_slave(ec_master_t *master, uint16_t slave_position,
+ ec_slave_info_t *slave_info)
+{
+ const ec_slave_t *slave;
+ unsigned int i;
+ int ret = 0;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ slave = ec_master_find_slave_const(master, 0, slave_position);
+
+ if (slave == NULL) {
+ ret = -ENOENT;
+ goto out_get_slave;
+ }
+
+ slave_info->position = slave->ring_position;
+ slave_info->vendor_id = slave->sii.vendor_id;
+ slave_info->product_code = slave->sii.product_code;
+ slave_info->revision_number = slave->sii.revision_number;
+ slave_info->serial_number = slave->sii.serial_number;
+ slave_info->alias = slave->effective_alias;
+ slave_info->current_on_ebus = slave->sii.current_on_ebus;
+
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ slave_info->ports[i].desc = slave->ports[i].desc;
+ slave_info->ports[i].link.link_up = slave->ports[i].link.link_up;
+ slave_info->ports[i].link.loop_closed =
+ slave->ports[i].link.loop_closed;
+ slave_info->ports[i].link.signal_detected =
+ slave->ports[i].link.signal_detected;
+ slave_info->ports[i].receive_time = slave->ports[i].receive_time;
+ if (slave->ports[i].next_slave) {
+ slave_info->ports[i].next_slave =
+ slave->ports[i].next_slave->ring_position;
+ } else {
+ slave_info->ports[i].next_slave = 0xffff;
+ }
+ slave_info->ports[i].delay_to_next_dc =
+ slave->ports[i].delay_to_next_dc;
+ }
+
+ slave_info->al_state = slave->current_state;
+ slave_info->error_flag = slave->error_flag;
+ slave_info->sync_count = slave->sii.sync_count;
+ slave_info->sdo_count = ec_slave_sdo_count(slave);
+ if (slave->sii.name) {
+ strncpy(slave_info->name, slave->sii.name, EC_MAX_STRING_LENGTH);
+ } else {
+ slave_info->name[0] = 0;
+ }
+
+out_get_slave:
+ up(&master->master_sem);
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_callbacks(ec_master_t *master,
+ void (*send_cb)(void *), void (*receive_cb)(void *), void *cb_data)
+{
+ EC_MASTER_DBG(master, 1, "ecrt_master_callbacks(master = 0x%p,"
+ " send_cb = 0x%p, receive_cb = 0x%p, cb_data = 0x%p)\n",
+ master, send_cb, receive_cb, cb_data);
+
+ master->app_send_cb = send_cb;
+ master->app_receive_cb = receive_cb;
+ master->app_cb_data = cb_data;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
+{
+ ec_device_index_t dev_idx;
+
+ state->slaves_responding = 0U;
+ state->al_states = 0;
+ state->link_up = 0U;
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ /* Announce sum of responding slaves on all links. */
+ state->slaves_responding += master->fsm.slaves_responding[dev_idx];
+
+ /* Binary-or slave states of all links. */
+ state->al_states |= master->fsm.slave_states[dev_idx];
+
+ /* Signal link up if at least one device has link. */
+ state->link_up |= master->devices[dev_idx].link_state;
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_master_link_state(const ec_master_t *master, unsigned int dev_idx,
+ ec_master_link_state_t *state)
+{
+ if (dev_idx >= ec_master_num_devices(master)) {
+ return -EINVAL;
+ }
+
+ state->slaves_responding = master->fsm.slaves_responding[dev_idx];
+ state->al_states = master->fsm.slave_states[dev_idx];
+ state->link_up = master->devices[dev_idx].link_state;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_application_time(ec_master_t *master, uint64_t app_time)
+{
+ master->app_time = app_time;
+
+ if (unlikely(!master->dc_ref_time)) {
+ master->dc_ref_time = app_time;
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_master_reference_clock_time(ec_master_t *master, uint32_t *time)
+{
+ if (!master->dc_ref_clock) {
+ return -ENXIO;
+ }
+
+ if (master->sync_datagram.state != EC_DATAGRAM_RECEIVED) {
+ return -EIO;
+ }
+
+ // Get returned datagram time, transmission delay removed.
+ *time = EC_READ_U32(master->sync_datagram.data) -
+ master->dc_ref_clock->transmission_delay;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_sync_reference_clock(ec_master_t *master)
+{
+ if (master->dc_ref_clock) {
+ EC_WRITE_U32(master->ref_sync_datagram.data, master->app_time);
+ ec_master_queue_datagram(master, &master->ref_sync_datagram);
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_master_sync_reference_clock_to(
+ ec_master_t *master,
+ uint64_t sync_time
+ )
+{
+ if (master->dc_ref_clock) {
+ EC_WRITE_U32(master->ref_sync_datagram.data, sync_time);
+ ec_master_queue_datagram(master, &master->ref_sync_datagram);
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_master_sync_slave_clocks(ec_master_t *master)
+{
+ if (master->dc_ref_clock) {
+ ec_datagram_zero(&master->sync_datagram);
+ ec_master_queue_datagram(master, &master->sync_datagram);
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_master_sync_monitor_queue(ec_master_t *master)
+{
+ ec_datagram_zero(&master->sync_mon_datagram);
+ ec_master_queue_datagram(master, &master->sync_mon_datagram);
+}
+
+/*****************************************************************************/
+
+uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
+{
+ if (master->sync_mon_datagram.state == EC_DATAGRAM_RECEIVED) {
+ return EC_READ_U32(master->sync_mon_datagram.data) & 0x7fffffff;
+ } else {
+ return 0xffffffff;
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_master_sdo_download(ec_master_t *master, uint16_t slave_position,
+ uint16_t index, uint8_t subindex, uint8_t *data,
+ size_t data_size, uint32_t *abort_code)
+{
+ ec_sdo_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ EC_MASTER_DBG(master, 1, "%s(master = 0x%p,"
+ " slave_position = %u, index = 0x%04X, subindex = 0x%02X,"
+ " data = 0x%p, data_size = %zu, abort_code = 0x%p)\n",
+ __func__, master, slave_position, index, subindex,
+ data, data_size, abort_code);
+
+ if (!data_size) {
+ EC_MASTER_ERR(master, "Zero data size!\n");
+ return -EINVAL;
+ }
+
+ ec_sdo_request_init(&request);
+ ecrt_sdo_request_index(&request, index, subindex);
+ ret = ec_sdo_request_alloc(&request, data_size);
+ if (ret) {
+ ec_sdo_request_clear(&request);
+ return ret;
+ }
+
+ memcpy(request.data, data, data_size);
+ request.data_size = data_size;
+ ecrt_sdo_request_write(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position);
+ ec_sdo_request_clear(&request);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling SDO download request.\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->sdo_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ *abort_code = request.abort_code;
+
+ if (request.state == EC_INT_REQUEST_SUCCESS) {
+ ret = 0;
+ } else if (request.errno) {
+ ret = -request.errno;
+ } else {
+ ret = -EIO;
+ }
+
+ ec_sdo_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_sdo_download_complete(ec_master_t *master,
+ uint16_t slave_position, uint16_t index, uint8_t *data,
+ size_t data_size, uint32_t *abort_code)
+{
+ ec_sdo_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ EC_MASTER_DBG(master, 1, "%s(master = 0x%p,"
+ " slave_position = %u, index = 0x%04X,"
+ " data = 0x%p, data_size = %zu, abort_code = 0x%p)\n",
+ __func__, master, slave_position, index, data, data_size,
+ abort_code);
+
+ if (!data_size) {
+ EC_MASTER_ERR(master, "Zero data size!\n");
+ return -EINVAL;
+ }
+
+ ec_sdo_request_init(&request);
+ ecrt_sdo_request_index(&request, index, 0);
+ ret = ec_sdo_request_alloc(&request, data_size);
+ if (ret) {
+ ec_sdo_request_clear(&request);
+ return ret;
+ }
+
+ request.complete_access = 1;
+ memcpy(request.data, data, data_size);
+ request.data_size = data_size;
+ ecrt_sdo_request_write(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position);
+ ec_sdo_request_clear(&request);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling SDO download request"
+ " (complete access).\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->sdo_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ *abort_code = request.abort_code;
+
+ if (request.state == EC_INT_REQUEST_SUCCESS) {
+ ret = 0;
+ } else if (request.errno) {
+ ret = -request.errno;
+ } else {
+ ret = -EIO;
+ }
+
+ ec_sdo_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_sdo_upload(ec_master_t *master, uint16_t slave_position,
+ uint16_t index, uint8_t subindex, uint8_t *target,
+ size_t target_size, size_t *result_size, uint32_t *abort_code)
+{
+ ec_sdo_request_t request;
+ ec_slave_t *slave;
+ int ret = 0;
+
+ EC_MASTER_DBG(master, 1, "%s(master = 0x%p,"
+ " slave_position = %u, index = 0x%04X, subindex = 0x%02X,"
+ " target = 0x%p, target_size = %zu, result_size = 0x%p,"
+ " abort_code = 0x%p)\n",
+ __func__, master, slave_position, index, subindex,
+ target, target_size, result_size, abort_code);
+
+ ec_sdo_request_init(&request);
+ ecrt_sdo_request_index(&request, index, subindex);
+ ecrt_sdo_request_read(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, slave_position))) {
+ up(&master->master_sem);
+ ec_sdo_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling SDO upload request.\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->sdo_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_sdo_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ *abort_code = request.abort_code;
+
+ if (request.state != EC_INT_REQUEST_SUCCESS) {
+ *result_size = 0;
+ if (request.errno) {
+ ret = -request.errno;
+ } else {
+ ret = -EIO;
+ }
+ } else {
+ if (request.data_size > target_size) {
+ EC_SLAVE_ERR(slave, "%s(): Buffer too small.\n", __func__);
+ ret = -EOVERFLOW;
+ }
+ else {
+ memcpy(target, request.data, request.data_size);
+ *result_size = request.data_size;
+ ret = 0;
+ }
+ }
+
+ ec_sdo_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_write_idn(ec_master_t *master, uint16_t slave_position,
+ uint8_t drive_no, uint16_t idn, uint8_t *data, size_t data_size,
+ uint16_t *error_code)
+{
+ ec_soe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (drive_no > 7) {
+ EC_MASTER_ERR(master, "Invalid drive number!\n");
+ return -EINVAL;
+ }
+
+ ec_soe_request_init(&request);
+ ec_soe_request_set_drive_no(&request, drive_no);
+ ec_soe_request_set_idn(&request, idn);
+
+ ret = ec_soe_request_alloc(&request, data_size);
+ if (ret) {
+ ec_soe_request_clear(&request);
+ return ret;
+ }
+
+ memcpy(request.data, data, data_size);
+ request.data_size = data_size;
+ ec_soe_request_write(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_soe_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ slave_position);
+ ec_soe_request_clear(&request);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling SoE write request.\n");
+
+ // schedule SoE write request.
+ list_add_tail(&request.list, &slave->soe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_soe_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ if (error_code) {
+ *error_code = request.error_code;
+ }
+ ret = request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+ ec_soe_request_clear(&request);
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+int ecrt_master_read_idn(ec_master_t *master, uint16_t slave_position,
+ uint8_t drive_no, uint16_t idn, uint8_t *target, size_t target_size,
+ size_t *result_size, uint16_t *error_code)
+{
+ ec_soe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (drive_no > 7) {
+ EC_MASTER_ERR(master, "Invalid drive number!\n");
+ return -EINVAL;
+ }
+
+ ec_soe_request_init(&request);
+ ec_soe_request_set_drive_no(&request, drive_no);
+ ec_soe_request_set_idn(&request, idn);
+ ec_soe_request_read(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_soe_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, slave_position))) {
+ up(&master->master_sem);
+ ec_soe_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", slave_position);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling SoE read request.\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->soe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_soe_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ if (error_code) {
+ *error_code = request.error_code;
+ }
+
+ if (request.state != EC_INT_REQUEST_SUCCESS) {
+ if (result_size) {
+ *result_size = 0;
+ }
+ ret = -EIO;
+ } else { // success
+ if (request.data_size > target_size) {
+ EC_SLAVE_ERR(slave, "%s(): Buffer too small.\n", __func__);
+ ret = -EOVERFLOW;
+ }
+ else { // data fits in buffer
+ if (result_size) {
+ *result_size = request.data_size;
+ }
+ memcpy(target, request.data, request.data_size);
+ ret = 0;
+ }
+ }
+
+ ec_soe_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+void ecrt_master_reset(ec_master_t *master)
+{
+ ec_slave_config_t *sc;
+
+ list_for_each_entry(sc, &master->configs, list) {
+ if (sc->slave) {
+ ec_slave_request_state(sc->slave, EC_SLAVE_STATE_OP);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_master_create_domain);
+EXPORT_SYMBOL(ecrt_master_activate);
+EXPORT_SYMBOL(ecrt_master_deactivate);
+EXPORT_SYMBOL(ecrt_master_send);
+EXPORT_SYMBOL(ecrt_master_send_ext);
+EXPORT_SYMBOL(ecrt_master_receive);
+EXPORT_SYMBOL(ecrt_master_callbacks);
+EXPORT_SYMBOL(ecrt_master);
+EXPORT_SYMBOL(ecrt_master_get_slave);
+EXPORT_SYMBOL(ecrt_master_slave_config);
+EXPORT_SYMBOL(ecrt_master_select_reference_clock);
+EXPORT_SYMBOL(ecrt_master_state);
+EXPORT_SYMBOL(ecrt_master_link_state);
+EXPORT_SYMBOL(ecrt_master_application_time);
+EXPORT_SYMBOL(ecrt_master_sync_reference_clock);
+EXPORT_SYMBOL(ecrt_master_sync_reference_clock_to);
+EXPORT_SYMBOL(ecrt_master_sync_slave_clocks);
+EXPORT_SYMBOL(ecrt_master_reference_clock_time);
+EXPORT_SYMBOL(ecrt_master_sync_monitor_queue);
+EXPORT_SYMBOL(ecrt_master_sync_monitor_process);
+EXPORT_SYMBOL(ecrt_master_sdo_download);
+EXPORT_SYMBOL(ecrt_master_sdo_download_complete);
+EXPORT_SYMBOL(ecrt_master_sdo_upload);
+EXPORT_SYMBOL(ecrt_master_write_idn);
+EXPORT_SYMBOL(ecrt_master_read_idn);
+EXPORT_SYMBOL(ecrt_master_reset);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/master.h b/net/ethercat/master/master.h
new file mode 100644
index 000000000000..98e9fdf91cac
--- /dev/null
+++ b/net/ethercat/master/master.h
@@ -0,0 +1,392 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_MASTER_H__
+#define __EC_MASTER_H__
+
+#include <linux/version.h>
+#include <linux/list.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
+#include <linux/kthread.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 27)
+#include <linux/semaphore.h>
+#else
+#include <asm/semaphore.h>
+#endif
+
+#include "device.h"
+#include "domain.h"
+#include "ethernet.h"
+#include "fsm_master.h"
+#include "cdev.h"
+
+#ifdef EC_RTDM
+#include "rtdm.h"
+#endif
+
+/*****************************************************************************/
+
+/** Convenience macro for printing master-specific information to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT <INDEX>: ",
+ * where INDEX is the master index.
+ *
+ * \param master EtherCAT master
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_MASTER_INFO(master, fmt, args...) \
+ printk(KERN_INFO "EtherCAT %u: " fmt, master->index, ##args)
+
+/** Convenience macro for printing master-specific errors to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT <INDEX>: ",
+ * where INDEX is the master index.
+ *
+ * \param master EtherCAT master
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_MASTER_ERR(master, fmt, args...) \
+ printk(KERN_ERR "EtherCAT ERROR %u: " fmt, master->index, ##args)
+
+/** Convenience macro for printing master-specific warnings to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT <INDEX>: ",
+ * where INDEX is the master index.
+ *
+ * \param master EtherCAT master
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_MASTER_WARN(master, fmt, args...) \
+ printk(KERN_WARNING "EtherCAT WARNING %u: " fmt, master->index, ##args)
+
+/** Convenience macro for printing master-specific debug messages to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed "EtherCAT <INDEX>: ",
+ * where INDEX is the master index.
+ *
+ * \param master EtherCAT master
+ * \param level Debug level. Master's debug level must be >= \a level for
+ * output.
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_MASTER_DBG(master, level, fmt, args...) \
+ do { \
+ if (master->debug_level >= level) { \
+ printk(KERN_DEBUG "EtherCAT DEBUG %u: " fmt, \
+ master->index, ##args); \
+ } \
+ } while (0)
+
+
+/** Size of the external datagram ring.
+ *
+ * The external datagram ring is used for slave FSMs.
+ */
+#define EC_EXT_RING_SIZE 32
+
+/*****************************************************************************/
+
+/** EtherCAT master phase.
+ */
+typedef enum {
+ EC_ORPHANED, /**< Orphaned phase. The master has no Ethernet device
+ attached. */
+ EC_IDLE, /**< Idle phase. An Ethernet device is attached, but the master
+ is not in use, yet. */
+ EC_OPERATION /**< Operation phase. The master was requested by a realtime
+ application. */
+} ec_master_phase_t;
+
+/*****************************************************************************/
+
+/** Cyclic statistics.
+ */
+typedef struct {
+ unsigned int timeouts; /**< datagram timeouts */
+ unsigned int corrupted; /**< corrupted frames */
+ unsigned int unmatched; /**< unmatched datagrams (received, but not
+ queued any longer) */
+ unsigned long output_jiffies; /**< time of last output */
+} ec_stats_t;
+
+/*****************************************************************************/
+
+/** Device statistics.
+ */
+typedef struct {
+ u64 tx_count; /**< Number of frames sent. */
+ u64 last_tx_count; /**< Number of frames sent of last statistics cycle. */
+ u64 rx_count; /**< Number of frames received. */
+ u64 last_rx_count; /**< Number of frames received of last statistics
+ cycle. */
+ u64 tx_bytes; /**< Number of bytes sent. */
+ u64 last_tx_bytes; /**< Number of bytes sent of last statistics cycle. */
+ u64 rx_bytes; /**< Number of bytes received. */
+ u64 last_rx_bytes; /**< Number of bytes received of last statistics cycle.
+ */
+ u64 last_loss; /**< Tx/Rx difference of last statistics cycle. */
+ s32 tx_frame_rates[EC_RATE_COUNT]; /**< Transmit rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 rx_frame_rates[EC_RATE_COUNT]; /**< Receive rates in frames/s for
+ different statistics cycle periods.
+ */
+ s32 tx_byte_rates[EC_RATE_COUNT]; /**< Transmit rates in byte/s for
+ different statistics cycle periods. */
+ s32 rx_byte_rates[EC_RATE_COUNT]; /**< Receive rates in byte/s for
+ different statistics cycle periods. */
+ s32 loss_rates[EC_RATE_COUNT]; /**< Frame loss rates for different
+ statistics cycle periods. */
+ unsigned long jiffies; /**< Jiffies of last statistic cycle. */
+} ec_device_stats_t;
+
+/*****************************************************************************/
+
+#if EC_MAX_NUM_DEVICES < 1
+#error Invalid number of devices
+#endif
+
+/*****************************************************************************/
+
+/** EtherCAT master.
+ *
+ * Manages slaves, domains and IO.
+ */
+struct ec_master {
+ unsigned int index; /**< Index. */
+ unsigned int reserved; /**< \a True, if the master is in use. */
+
+ ec_cdev_t cdev; /**< Master character device. */
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)
+ struct device *class_device; /**< Master class device. */
+#else
+ struct class_device *class_device; /**< Master class device. */
+#endif
+
+#ifdef EC_RTDM
+ ec_rtdm_dev_t rtdm_dev; /**< RTDM device. */
+#endif
+
+ struct semaphore master_sem; /**< Master semaphore. */
+
+ ec_device_t devices[EC_MAX_NUM_DEVICES]; /**< EtherCAT devices. */
+ const uint8_t *macs[EC_MAX_NUM_DEVICES]; /**< Device MAC addresses. */
+#if EC_MAX_NUM_DEVICES > 1
+ unsigned int num_devices; /**< Number of devices. Access this always via
+ ec_master_num_devices(), because it may be
+ optimized! */
+#endif
+ struct semaphore device_sem; /**< Device semaphore. */
+ ec_device_stats_t device_stats; /**< Device statistics. */
+
+ ec_fsm_master_t fsm; /**< Master state machine. */
+ ec_datagram_t fsm_datagram; /**< Datagram used for state machines. */
+ ec_master_phase_t phase; /**< Master phase. */
+ unsigned int active; /**< Master has been activated. */
+ unsigned int config_changed; /**< The configuration changed. */
+ unsigned int injection_seq_fsm; /**< Datagram injection sequence number
+ for the FSM side. */
+ unsigned int injection_seq_rt; /**< Datagram injection sequence number
+ for the realtime side. */
+
+ ec_slave_t *slaves; /**< Array of slaves on the bus. */
+ unsigned int slave_count; /**< Number of slaves on the bus. */
+
+ /* Configuration applied by the application. */
+ struct list_head configs; /**< List of slave configurations. */
+ struct list_head domains; /**< List of domains. */
+
+ u64 app_time; /**< Time of the last ecrt_master_sync() call. */
+ u64 dc_ref_time; /**< Common reference timestamp for DC start times. */
+ ec_datagram_t ref_sync_datagram; /**< Datagram used for synchronizing the
+ reference clock to the master clock. */
+ ec_datagram_t sync_datagram; /**< Datagram used for DC drift
+ compensation. */
+ ec_datagram_t sync_mon_datagram; /**< Datagram used for DC synchronisation
+ monitoring. */
+ ec_slave_config_t *dc_ref_config; /**< Application-selected DC reference
+ clock slave config. */
+ ec_slave_t *dc_ref_clock; /**< DC reference clock slave. */
+
+ unsigned int scan_busy; /**< Current scan state. */
+ unsigned int allow_scan; /**< \a True, if slave scanning is allowed. */
+ struct semaphore scan_sem; /**< Semaphore protecting the \a scan_busy
+ variable and the \a allow_scan flag. */
+ wait_queue_head_t scan_queue; /**< Queue for processes that wait for
+ slave scanning. */
+
+ unsigned int config_busy; /**< State of slave configuration. */
+ struct semaphore config_sem; /**< Semaphore protecting the \a config_busy
+ variable and the allow_config flag. */
+ wait_queue_head_t config_queue; /**< Queue for processes that wait for
+ slave configuration. */
+
+ struct list_head datagram_queue; /**< Datagram queue. */
+ uint8_t datagram_index; /**< Current datagram index. */
+
+ struct list_head ext_datagram_queue; /**< Queue for non-application
+ datagrams. */
+ struct semaphore ext_queue_sem; /**< Semaphore protecting the \a
+ ext_datagram_queue. */
+
+ ec_datagram_t ext_datagram_ring[EC_EXT_RING_SIZE]; /**< External datagram
+ ring. */
+ unsigned int ext_ring_idx_rt; /**< Index in external datagram ring for RT
+ side. */
+ unsigned int ext_ring_idx_fsm; /**< Index in external datagram ring for
+ FSM side. */
+ unsigned int send_interval; /**< Interval between two calls to
+ ecrt_master_send(). */
+ size_t max_queue_size; /**< Maximum size of datagram queue */
+
+ ec_slave_t *fsm_slave; /**< Slave that is queried next for FSM exec. */
+ struct list_head fsm_exec_list; /**< Slave FSM execution list. */
+ unsigned int fsm_exec_count; /**< Number of entries in execution list. */
+
+ unsigned int debug_level; /**< Master debug level. */
+ ec_stats_t stats; /**< Cyclic statistics. */
+
+ struct task_struct *thread; /**< Master thread. */
+
+#ifdef EC_EOE
+ struct task_struct *eoe_thread; /**< EoE thread. */
+ struct list_head eoe_handlers; /**< Ethernet over EtherCAT handlers. */
+#endif
+
+ struct semaphore io_sem; /**< Semaphore used in \a IDLE phase. */
+
+ void (*send_cb)(void *); /**< Current send datagrams callback. */
+ void (*receive_cb)(void *); /**< Current receive datagrams callback. */
+ void *cb_data; /**< Current callback data. */
+ void (*app_send_cb)(void *); /**< Application's send datagrams
+ callback. */
+ void (*app_receive_cb)(void *); /**< Application's receive datagrams
+ callback. */
+ void *app_cb_data; /**< Application callback data. */
+
+ struct list_head sii_requests; /**< SII write requests. */
+ struct list_head emerg_reg_requests; /**< Emergency register access
+ requests. */
+
+ wait_queue_head_t request_queue; /**< Wait queue for external requests
+ from user space. */
+};
+
+/*****************************************************************************/
+
+// static funtions
+void ec_master_init_static(void);
+
+// master creation/deletion
+int ec_master_init(ec_master_t *, unsigned int, const uint8_t *,
+ const uint8_t *, dev_t, struct class *, unsigned int);
+void ec_master_clear(ec_master_t *);
+
+/** Number of Ethernet devices.
+ */
+#if EC_MAX_NUM_DEVICES > 1
+#define ec_master_num_devices(MASTER) ((MASTER)->num_devices)
+#else
+#define ec_master_num_devices(MASTER) 1
+#endif
+
+// phase transitions
+int ec_master_enter_idle_phase(ec_master_t *);
+void ec_master_leave_idle_phase(ec_master_t *);
+int ec_master_enter_operation_phase(ec_master_t *);
+void ec_master_leave_operation_phase(ec_master_t *);
+
+#ifdef EC_EOE
+// EoE
+void ec_master_eoe_start(ec_master_t *);
+void ec_master_eoe_stop(ec_master_t *);
+#endif
+
+// datagram IO
+void ec_master_receive_datagrams(ec_master_t *, ec_device_t *,
+ const uint8_t *, size_t);
+void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
+void ec_master_queue_datagram_ext(ec_master_t *, ec_datagram_t *);
+
+// misc.
+void ec_master_set_send_interval(ec_master_t *, unsigned int);
+void ec_master_attach_slave_configs(ec_master_t *);
+ec_slave_t *ec_master_find_slave(ec_master_t *, uint16_t, uint16_t);
+const ec_slave_t *ec_master_find_slave_const(const ec_master_t *, uint16_t,
+ uint16_t);
+void ec_master_output_stats(ec_master_t *);
+#ifdef EC_EOE
+void ec_master_clear_eoe_handlers(ec_master_t *);
+#endif
+void ec_master_clear_slaves(ec_master_t *);
+
+unsigned int ec_master_config_count(const ec_master_t *);
+ec_slave_config_t *ec_master_get_config(
+ const ec_master_t *, unsigned int);
+const ec_slave_config_t *ec_master_get_config_const(
+ const ec_master_t *, unsigned int);
+unsigned int ec_master_domain_count(const ec_master_t *);
+ec_domain_t *ec_master_find_domain(ec_master_t *, unsigned int);
+const ec_domain_t *ec_master_find_domain_const(const ec_master_t *,
+ unsigned int);
+#ifdef EC_EOE
+uint16_t ec_master_eoe_handler_count(const ec_master_t *);
+const ec_eoe_t *ec_master_get_eoe_handler_const(const ec_master_t *, uint16_t);
+#endif
+
+int ec_master_debug_level(ec_master_t *, unsigned int);
+
+ec_domain_t *ecrt_master_create_domain_err(ec_master_t *);
+ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *, uint16_t,
+ uint16_t, uint32_t, uint32_t);
+
+void ec_master_calc_dc(ec_master_t *);
+void ec_master_request_op(ec_master_t *);
+
+void ec_master_internal_send_cb(void *);
+void ec_master_internal_receive_cb(void *);
+
+extern const unsigned int rate_intervals[EC_RATE_COUNT]; // see master.c
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/module.c b/net/ethercat/master/module.c
new file mode 100644
index 000000000000..dfbad367dd88
--- /dev/null
+++ b/net/ethercat/master/module.c
@@ -0,0 +1,675 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 master driver module.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/err.h>
+
+#include "globals.h"
+#include "master.h"
+#include "device.h"
+
+/*****************************************************************************/
+
+#define MAX_MASTERS 32 /**< Maximum number of masters. */
+
+/*****************************************************************************/
+
+int __init ec_init_module(void);
+void __exit ec_cleanup_module(void);
+
+static int ec_mac_parse(uint8_t *, const char *, int);
+
+/*****************************************************************************/
+
+static char *main_devices[MAX_MASTERS] = {"6c:cf:39:00:27:47", }; /**< Main devices parameter. */
+static unsigned int master_count = 1; /**< Number of masters. */
+static char *backup_devices[MAX_MASTERS]; /**< Backup devices parameter. */
+static unsigned int backup_count; /**< Number of backup devices. */
+static unsigned int debug_level = 0; /**< Debug level parameter. */
+
+static ec_master_t *masters; /**< Array of masters. */
+static struct semaphore master_sem; /**< Master semaphore. */
+
+dev_t device_number; /**< Device number for master cdevs. */
+struct class *class; /**< Device class. */
+
+static uint8_t macs[MAX_MASTERS][2][ETH_ALEN]; /**< MAC addresses. */
+
+char *ec_master_version_str = EC_MASTER_VERSION; /**< Version string. */
+
+/*****************************************************************************/
+
+/** \cond */
+
+MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
+MODULE_DESCRIPTION("EtherCAT master driver module");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(EC_MASTER_VERSION);
+
+module_param_array(main_devices, charp, &master_count, S_IRUGO);
+MODULE_PARM_DESC(main_devices, "MAC addresses of main devices");
+module_param_array(backup_devices, charp, &backup_count, S_IRUGO);
+MODULE_PARM_DESC(backup_devices, "MAC addresses of backup devices");
+module_param_named(debug_level, debug_level, uint, S_IRUGO);
+MODULE_PARM_DESC(debug_level, "Debug level");
+
+/** \endcond */
+
+/*****************************************************************************/
+
+/** Module initialization.
+ *
+ * Initializes \a master_count masters.
+ * \return 0 on success, else < 0
+ */
+int __init ec_init_module(void)
+{
+ int i, ret = 0;
+
+ EC_INFO("Master driver %s\n", EC_MASTER_VERSION);
+
+ sema_init(&master_sem, 1);
+
+ if (master_count) {
+ if (alloc_chrdev_region(&device_number,
+ 0, master_count, "EtherCAT")) {
+ EC_ERR("Failed to obtain device number(s)!\n");
+ ret = -EBUSY;
+ goto out_return;
+ }
+ }
+
+ class = class_create(THIS_MODULE, "EtherCAT");
+ if (IS_ERR(class)) {
+ EC_ERR("Failed to create device class.\n");
+ ret = PTR_ERR(class);
+ goto out_cdev;
+ }
+
+ // zero MAC addresses
+ memset(macs, 0x00, sizeof(uint8_t) * MAX_MASTERS * 2 * ETH_ALEN);
+
+ // process MAC parameters
+ for (i = 0; i < master_count; i++) {
+ ret = ec_mac_parse(macs[i][0], main_devices[i], 0);
+ if (ret)
+ goto out_class;
+
+ if (i < backup_count) {
+ ret = ec_mac_parse(macs[i][1], backup_devices[i], 1);
+ if (ret)
+ goto out_class;
+ }
+ }
+ EC_INFO("emaster init\n");
+
+ // initialize static master variables
+ ec_master_init_static();
+
+ if (master_count) {
+ if (!(masters = kmalloc(sizeof(ec_master_t) * master_count,
+ GFP_KERNEL))) {
+ EC_ERR("Failed to allocate memory"
+ " for EtherCAT masters.\n");
+ ret = -ENOMEM;
+ goto out_class;
+ }
+ }
+
+ for (i = 0; i < master_count; i++) {
+ ret = ec_master_init(&masters[i], i, macs[i][0], macs[i][1],
+ device_number, class, debug_level);
+ if (ret)
+ goto out_free_masters;
+ }
+
+ EC_INFO("%u master%s waiting for devices.\n",
+ master_count, (master_count == 1 ? "" : "s"));
+ return ret;
+
+out_free_masters:
+ for (i--; i >= 0; i--)
+ ec_master_clear(&masters[i]);
+ kfree(masters);
+out_class:
+ class_destroy(class);
+out_cdev:
+ if (master_count)
+ unregister_chrdev_region(device_number, master_count);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Module cleanup.
+ *
+ * Clears all master instances.
+ */
+void __exit ec_cleanup_module(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < master_count; i++) {
+ ec_master_clear(&masters[i]);
+ }
+
+ if (master_count)
+ kfree(masters);
+
+ class_destroy(class);
+
+ if (master_count)
+ unregister_chrdev_region(device_number, master_count);
+
+ EC_INFO("Master module cleaned up.\n");
+}
+
+/*****************************************************************************/
+
+/** Get the number of masters.
+ */
+unsigned int ec_master_count(void)
+{
+ return master_count;
+}
+
+/*****************************************************************************
+ * MAC address functions
+ ****************************************************************************/
+
+/**
+ * \return true, if two MAC addresses are equal.
+ */
+int ec_mac_equal(
+ const uint8_t *mac1, /**< First MAC address. */
+ const uint8_t *mac2 /**< Second MAC address. */
+ )
+{
+ unsigned int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ if (mac1[i] != mac2[i])
+ return 0;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Maximum MAC string size.
+ */
+#define EC_MAX_MAC_STRING_SIZE (3 * ETH_ALEN)
+
+/** Print a MAC address to a buffer.
+ *
+ * The buffer size must be at least EC_MAX_MAC_STRING_SIZE.
+ *
+ * \return number of bytes written.
+ */
+ssize_t ec_mac_print(
+ const uint8_t *mac, /**< MAC address */
+ char *buffer /**< Target buffer. */
+ )
+{
+ off_t off = 0;
+ unsigned int i;
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ off += sprintf(buffer + off, "%02X", mac[i]);
+ if (i < ETH_ALEN - 1) off += sprintf(buffer + off, ":");
+ }
+
+ return off;
+}
+
+/*****************************************************************************/
+
+/**
+ * \return true, if the MAC address is all-zero.
+ */
+int ec_mac_is_zero(
+ const uint8_t *mac /**< MAC address. */
+ )
+{
+ unsigned int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ if (mac[i])
+ return 0;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/**
+ * \return true, if the given MAC address is the broadcast address.
+ */
+int ec_mac_is_broadcast(
+ const uint8_t *mac /**< MAC address. */
+ )
+{
+ unsigned int i;
+
+ for (i = 0; i < ETH_ALEN; i++)
+ if (mac[i] != 0xff)
+ return 0;
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Parse a MAC address from a string.
+ *
+ * The MAC address must match the regular expression
+ * "([0-9a-fA-F]{2}:){5}[0-9a-fA-F]{2}".
+ *
+ * \return 0 on success, else < 0
+ */
+static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty)
+{
+ unsigned int i, value;
+ const char *orig = src;
+ char *rem;
+
+ if (!strlen(src)) {
+ if (allow_empty){
+ return 0;
+ } else {
+ EC_ERR("MAC address may not be empty.\n");
+ return -EINVAL;
+ }
+ }
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ value = simple_strtoul(src, &rem, 16);
+ if (rem != src + 2
+ || value > 0xFF
+ || (i < ETH_ALEN - 1 && *rem != ':')) {
+ EC_ERR("Invalid MAC address \"%s\".\n", orig);
+ return -EINVAL;
+ }
+ mac[i] = value;
+ if (i < ETH_ALEN - 1) {
+ src = rem + 1; // skip colon
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Outputs frame contents for debugging purposes.
+ * If the data block is larger than 256 bytes, only the first 128
+ * and the last 128 bytes will be shown
+ */
+void ec_print_data(const uint8_t *data, /**< pointer to data */
+ size_t size /**< number of bytes to output */
+ )
+{
+ unsigned int i;
+
+ EC_DBG("");
+ for (i = 0; i < size; i++) {
+ printk("%02X ", data[i]);
+
+ if ((i + 1) % 16 == 0 && i < size - 1) {
+ printk("\n");
+ EC_DBG("");
+ }
+
+ if (i + 1 == 128 && size > 256) {
+ printk("dropped %zu bytes\n", size - 128 - i);
+ i = size - 128;
+ EC_DBG("");
+ }
+ }
+ printk("\n");
+}
+
+/*****************************************************************************/
+
+/** Outputs frame contents and differences for debugging purposes.
+ */
+void ec_print_data_diff(const uint8_t *d1, /**< first data */
+ const uint8_t *d2, /**< second data */
+ size_t size /** number of bytes to output */
+ )
+{
+ unsigned int i;
+
+ EC_DBG("");
+ for (i = 0; i < size; i++) {
+ if (d1[i] == d2[i]) printk(".. ");
+ else printk("%02X ", d2[i]);
+ if ((i + 1) % 16 == 0) {
+ printk("\n");
+ EC_DBG("");
+ }
+ }
+ printk("\n");
+}
+
+/*****************************************************************************/
+
+/** Prints slave states in clear text.
+ *
+ * \return Size of the created string.
+ */
+size_t ec_state_string(uint8_t states, /**< slave states */
+ char *buffer, /**< target buffer
+ (min. EC_STATE_STRING_SIZE bytes) */
+ uint8_t multi /**< Show multi-state mask. */
+ )
+{
+ off_t off = 0;
+ unsigned int first = 1;
+
+ if (!states) {
+ off += sprintf(buffer + off, "(unknown)");
+ return off;
+ }
+
+ if (multi) { // multiple slaves
+ if (states & EC_SLAVE_STATE_INIT) {
+ off += sprintf(buffer + off, "INIT");
+ first = 0;
+ }
+ if (states & EC_SLAVE_STATE_PREOP) {
+ if (!first) off += sprintf(buffer + off, ", ");
+ off += sprintf(buffer + off, "PREOP");
+ first = 0;
+ }
+ if (states & EC_SLAVE_STATE_SAFEOP) {
+ if (!first) off += sprintf(buffer + off, ", ");
+ off += sprintf(buffer + off, "SAFEOP");
+ first = 0;
+ }
+ if (states & EC_SLAVE_STATE_OP) {
+ if (!first) off += sprintf(buffer + off, ", ");
+ off += sprintf(buffer + off, "OP");
+ }
+ } else { // single slave
+ if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_INIT) {
+ off += sprintf(buffer + off, "INIT");
+ } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_PREOP) {
+ off += sprintf(buffer + off, "PREOP");
+ } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_BOOT) {
+ off += sprintf(buffer + off, "BOOT");
+ } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_SAFEOP) {
+ off += sprintf(buffer + off, "SAFEOP");
+ } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_OP) {
+ off += sprintf(buffer + off, "OP");
+ } else {
+ off += sprintf(buffer + off, "(invalid)");
+ }
+ first = 0;
+ }
+
+ if (states & EC_SLAVE_STATE_ACK_ERR) {
+ if (!first) off += sprintf(buffer + off, " + ");
+ off += sprintf(buffer + off, "ERROR");
+ }
+
+ return off;
+}
+
+/******************************************************************************
+ * Device interface
+ *****************************************************************************/
+
+/** Device names.
+ */
+const char *ec_device_names[2] = {
+ "main",
+ "backup"
+};
+
+/** Offers an EtherCAT device to a certain master.
+ *
+ * The master decides, if it wants to use the device for EtherCAT operation,
+ * or not. It is important, that the offered net_device is not used by the
+ * kernel IP stack. If the master, accepted the offer, the address of the
+ * newly created EtherCAT device is returned, else \a NULL is returned.
+ *
+ * \return Pointer to device, if accepted, or NULL if declined.
+ * \ingroup DeviceInterface
+ */
+ec_device_t *ecdev_offer(
+ struct net_device *net_dev, /**< net_device to offer */
+ ec_pollfunc_t poll, /**< device poll function */
+ struct module *module /**< pointer to the module */
+ )
+{
+ ec_master_t *master;
+ char str[EC_MAX_MAC_STRING_SIZE];
+ unsigned int i, dev_idx;
+
+ for (i = 0; i < master_count; i++) {
+ master = &masters[i];
+ ec_mac_print(net_dev->dev_addr, str);
+
+ if (down_interruptible(&master->device_sem)) {
+ EC_MASTER_WARN(master, "%s() interrupted!\n", __func__);
+ return NULL;
+ }
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(master); dev_idx++) {
+ if (!master->devices[dev_idx].dev
+ && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr)
+ || ec_mac_is_broadcast(master->macs[dev_idx]))) {
+
+ EC_INFO("Accepting %s as %s device for master %u.\n",
+ str, ec_device_names[dev_idx != 0], master->index);
+
+ ec_device_attach(&master->devices[dev_idx],
+ net_dev, poll, module);
+ up(&master->device_sem);
+
+ snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
+ ec_device_names[dev_idx != 0][0], master->index);
+
+ return &master->devices[dev_idx]; // offer accepted
+ }
+ }
+
+ up(&master->device_sem);
+
+ EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
+ }
+
+ return NULL; // offer declined
+}
+
+/******************************************************************************
+ * Application interface
+ *****************************************************************************/
+
+/** Request a master.
+ *
+ * Same as ecrt_request_master(), but with ERR_PTR() return value.
+ *
+ * \return Requested master.
+ */
+ec_master_t *ecrt_request_master_err(
+ unsigned int master_index /**< Master index. */
+ )
+{
+ ec_master_t *master, *errptr = NULL;
+ unsigned int dev_idx = EC_DEVICE_MAIN;
+
+ EC_INFO("Requesting master %u...\n", master_index);
+
+ if (master_index >= master_count) {
+ EC_ERR("Invalid master index %u.\n", master_index);
+ errptr = ERR_PTR(-EINVAL);
+ goto out_return;
+ }
+ master = &masters[master_index];
+
+ if (down_interruptible(&master_sem)) {
+ errptr = ERR_PTR(-EINTR);
+ goto out_return;
+ }
+
+ if (master->reserved) {
+ up(&master_sem);
+ EC_MASTER_ERR(master, "Master already in use!\n");
+ errptr = ERR_PTR(-EBUSY);
+ goto out_return;
+ }
+ master->reserved = 1;
+ up(&master_sem);
+
+ if (down_interruptible(&master->device_sem)) {
+ errptr = ERR_PTR(-EINTR);
+ goto out_release;
+ }
+
+ if (master->phase != EC_IDLE) {
+ up(&master->device_sem);
+ EC_MASTER_ERR(master, "Master still waiting for devices!\n");
+ errptr = ERR_PTR(-ENODEV);
+ goto out_release;
+ }
+
+ for (; dev_idx < ec_master_num_devices(master); dev_idx++) {
+ ec_device_t *device = &master->devices[dev_idx];
+ if (!try_module_get(device->module)) {
+ up(&master->device_sem);
+ EC_MASTER_ERR(master, "Device module is unloading!\n");
+ errptr = ERR_PTR(-ENODEV);
+ goto out_module_put;
+ }
+ }
+
+ up(&master->device_sem);
+
+ if (ec_master_enter_operation_phase(master)) {
+ EC_MASTER_ERR(master, "Failed to enter OPERATION phase!\n");
+ errptr = ERR_PTR(-EIO);
+ goto out_module_put;
+ }
+
+ EC_INFO("Successfully requested master %u.\n", master_index);
+ return master;
+
+ out_module_put:
+ for (; dev_idx > 0; dev_idx--) {
+ ec_device_t *device = &master->devices[dev_idx - 1];
+ module_put(device->module);
+ }
+ out_release:
+ master->reserved = 0;
+ out_return:
+ return errptr;
+}
+
+/*****************************************************************************/
+
+ec_master_t *ecrt_request_master(unsigned int master_index)
+{
+ ec_master_t *master = ecrt_request_master_err(master_index);
+ return IS_ERR(master) ? NULL : master;
+}
+
+/*****************************************************************************/
+
+void ecrt_release_master(ec_master_t *master)
+{
+ unsigned int dev_idx;
+
+ EC_MASTER_INFO(master, "Releasing master...\n");
+
+ if (!master->reserved) {
+ EC_MASTER_WARN(master, "%s(): Master was was not requested!\n",
+ __func__);
+ return;
+ }
+
+ ec_master_leave_operation_phase(master);
+
+ for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
+ dev_idx++) {
+ module_put(master->devices[dev_idx].module);
+ }
+
+ master->reserved = 0;
+
+ EC_MASTER_INFO(master, "Released.\n");
+}
+
+/*****************************************************************************/
+
+unsigned int ecrt_version_magic(void)
+{
+ return ECRT_VERSION_MAGIC;
+}
+
+/*****************************************************************************/
+
+/** Global request state type translation table.
+ *
+ * Translates an internal request state to an external one.
+ */
+const ec_request_state_t ec_request_state_translation_table[] = {
+ EC_REQUEST_UNUSED, // EC_INT_REQUEST_INIT,
+ EC_REQUEST_BUSY, // EC_INT_REQUEST_QUEUED,
+ EC_REQUEST_BUSY, // EC_INT_REQUEST_BUSY,
+ EC_REQUEST_SUCCESS, // EC_INT_REQUEST_SUCCESS,
+ EC_REQUEST_ERROR // EC_INT_REQUEST_FAILURE
+};
+
+/*****************************************************************************/
+
+/** \cond */
+
+module_init(ec_init_module);
+module_exit(ec_cleanup_module);
+
+EXPORT_SYMBOL(ecdev_offer);
+
+EXPORT_SYMBOL(ecrt_request_master);
+EXPORT_SYMBOL(ecrt_release_master);
+EXPORT_SYMBOL(ecrt_version_magic);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/pdo.c b/net/ethercat/master/pdo.c
new file mode 100644
index 000000000000..1597835cc48b
--- /dev/null
+++ b/net/ethercat/master/pdo.c
@@ -0,0 +1,317 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 process data object methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+#include <linux/err.h>
+
+#include "pdo.h"
+
+/*****************************************************************************/
+
+/** PDO constructor.
+ */
+void ec_pdo_init(
+ ec_pdo_t *pdo /**< EtherCAT PDO */
+ )
+{
+ pdo->sync_index = -1; // not assigned
+ pdo->name = NULL;
+ INIT_LIST_HEAD(&pdo->entries);
+}
+
+/*****************************************************************************/
+
+/** PDO copy constructor.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_pdo_init_copy(
+ ec_pdo_t *pdo, /**< PDO to create. */
+ const ec_pdo_t *other_pdo /**< PDO to copy from. */
+ )
+{
+ int ret = 0;
+
+ pdo->index = other_pdo->index;
+ pdo->sync_index = other_pdo->sync_index;
+ pdo->name = NULL;
+ INIT_LIST_HEAD(&pdo->entries);
+
+ ret = ec_pdo_set_name(pdo, other_pdo->name);
+ if (ret < 0)
+ goto out_return;
+
+ ret = ec_pdo_copy_entries(pdo, other_pdo);
+ if (ret < 0)
+ goto out_clear;
+
+ return 0;
+
+out_clear:
+ ec_pdo_clear(pdo);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** PDO destructor.
+ */
+void ec_pdo_clear(ec_pdo_t *pdo /**< EtherCAT PDO. */)
+{
+ if (pdo->name)
+ kfree(pdo->name);
+
+ ec_pdo_clear_entries(pdo);
+}
+
+/*****************************************************************************/
+
+/** Clear PDO entry list.
+ */
+void ec_pdo_clear_entries(ec_pdo_t *pdo /**< EtherCAT PDO. */)
+{
+ ec_pdo_entry_t *entry, *next;
+
+ // free all PDO entries
+ list_for_each_entry_safe(entry, next, &pdo->entries, list) {
+ list_del(&entry->list);
+ ec_pdo_entry_clear(entry);
+ kfree(entry);
+ }
+}
+
+/*****************************************************************************/
+
+/** Set PDO name.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_pdo_set_name(
+ ec_pdo_t *pdo, /**< PDO. */
+ const char *name /**< New name. */
+ )
+{
+ unsigned int len;
+
+ if (pdo->name && name && !strcmp(pdo->name, name))
+ return 0;
+
+ if (pdo->name)
+ kfree(pdo->name);
+
+ if (name && (len = strlen(name))) {
+ if (!(pdo->name = (char *) kmalloc(len + 1, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate PDO name.\n");
+ return -ENOMEM;
+ }
+ memcpy(pdo->name, name, len + 1);
+ } else {
+ pdo->name = NULL;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Add a new PDO entry to the configuration.
+ *
+ * \retval Pointer to the added entry, otherwise a ERR_PTR() code.
+ */
+ec_pdo_entry_t *ec_pdo_add_entry(
+ ec_pdo_t *pdo, /**< PDO. */
+ uint16_t index, /**< New entry's index. */
+ uint8_t subindex, /**< New entry's subindex. */
+ uint8_t bit_length /**< New entry's bit length. */
+ )
+{
+ ec_pdo_entry_t *entry;
+
+ if (!(entry = kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
+ EC_ERR("Failed to allocate memory for PDO entry.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ec_pdo_entry_init(entry);
+ entry->index = index;
+ entry->subindex = subindex;
+ entry->bit_length = bit_length;
+ list_add_tail(&entry->list, &pdo->entries);
+ return entry;
+}
+
+/*****************************************************************************/
+
+/** Copy PDO entries from another PDO.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_pdo_copy_entries(
+ ec_pdo_t *pdo, /**< PDO whos entries shall be replaced. */
+ const ec_pdo_t *other /**< Pdo with entries to copy. */
+ )
+{
+ ec_pdo_entry_t *entry, *other_entry;
+ int ret;
+
+ ec_pdo_clear_entries(pdo);
+
+ list_for_each_entry(other_entry, &other->entries, list) {
+ if (!(entry = (ec_pdo_entry_t *)
+ kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
+ EC_ERR("Failed to allocate memory for PDO entry copy.\n");
+ return -ENOMEM;
+ }
+
+ ret = ec_pdo_entry_init_copy(entry, other_entry);
+ if (ret < 0) {
+ kfree(entry);
+ return ret;
+ }
+
+ list_add_tail(&entry->list, &pdo->entries);
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Compares the entries of two PDOs.
+ *
+ * \retval 1 The entries of the given PDOs are equal.
+ * \retval 0 The entries of the given PDOs differ.
+ */
+int ec_pdo_equal_entries(
+ const ec_pdo_t *pdo1, /**< First PDO. */
+ const ec_pdo_t *pdo2 /**< Second PDO. */
+ )
+{
+ const struct list_head *head1, *head2, *item1, *item2;
+ const ec_pdo_entry_t *entry1, *entry2;
+
+ head1 = item1 = &pdo1->entries;
+ head2 = item2 = &pdo2->entries;
+
+ while (1) {
+ item1 = item1->next;
+ item2 = item2->next;
+
+ if ((item1 == head1) ^ (item2 == head2)) // unequal lengths
+ return 0;
+ if (item1 == head1) // both finished
+ break;
+
+ entry1 = list_entry(item1, ec_pdo_entry_t, list);
+ entry2 = list_entry(item2, ec_pdo_entry_t, list);
+ if (!ec_pdo_entry_equal(entry1, entry2))
+ return 0;
+ }
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Get the number of PDO entries.
+ *
+ * \return Number of PDO entries.
+ */
+unsigned int ec_pdo_entry_count(
+ const ec_pdo_t *pdo /**< PDO. */
+ )
+{
+ const ec_pdo_entry_t *entry;
+ unsigned int num = 0;
+
+ list_for_each_entry(entry, &pdo->entries, list) {
+ num++;
+ }
+
+ return num;
+}
+
+/*****************************************************************************/
+
+/** Finds a PDO entry via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Search result, or NULL.
+ */
+const ec_pdo_entry_t *ec_pdo_find_entry_by_pos_const(
+ const ec_pdo_t *pdo, /**< PDO. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ const ec_pdo_entry_t *entry;
+
+ list_for_each_entry(entry, &pdo->entries, list) {
+ if (pos--)
+ continue;
+ return entry;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Outputs the PDOs in the list.
+ */
+void ec_pdo_print_entries(
+ const ec_pdo_t *pdo /**< PDO. */
+ )
+{
+ const ec_pdo_entry_t *entry;
+
+ if (list_empty(&pdo->entries)) {
+ printk("(none)");
+ } else {
+ list_for_each_entry(entry, &pdo->entries, list) {
+ printk("0x%04X:%02X/%u",
+ entry->index, entry->subindex, entry->bit_length);
+ if (entry->list.next != &pdo->entries)
+ printk(" ");
+ }
+ }
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/pdo.h b/net/ethercat/master/pdo.h
new file mode 100644
index 000000000000..b84a1b7ae1a3
--- /dev/null
+++ b/net/ethercat/master/pdo.h
@@ -0,0 +1,75 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 Process data object structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_PDO_H__
+#define __EC_PDO_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "pdo_entry.h"
+
+/*****************************************************************************/
+
+/** PDO description.
+ */
+typedef struct {
+ struct list_head list; /**< List item. */
+ uint16_t index; /**< PDO index. */
+ int8_t sync_index; /**< Assigned sync manager. \todo remove? */
+ char *name; /**< PDO name. */
+ struct list_head entries; /**< List of PDO entries. */
+} ec_pdo_t;
+
+/*****************************************************************************/
+
+void ec_pdo_init(ec_pdo_t *);
+int ec_pdo_init_copy(ec_pdo_t *, const ec_pdo_t *);
+void ec_pdo_clear(ec_pdo_t *);
+void ec_pdo_clear_entries(ec_pdo_t *);
+int ec_pdo_set_name(ec_pdo_t *, const char *);
+ec_pdo_entry_t *ec_pdo_add_entry(ec_pdo_t *, uint16_t, uint8_t, uint8_t);
+int ec_pdo_copy_entries(ec_pdo_t *, const ec_pdo_t *);
+int ec_pdo_equal_entries(const ec_pdo_t *, const ec_pdo_t *);
+unsigned int ec_pdo_entry_count(const ec_pdo_t *);
+const ec_pdo_entry_t *ec_pdo_find_entry_by_pos_const(
+ const ec_pdo_t *, unsigned int);
+
+void ec_pdo_print_entries(const ec_pdo_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/pdo_entry.c b/net/ethercat/master/pdo_entry.c
new file mode 100644
index 000000000000..036a75cb0b60
--- /dev/null
+++ b/net/ethercat/master/pdo_entry.c
@@ -0,0 +1,132 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 process data object entry methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "pdo_entry.h"
+
+/*****************************************************************************/
+
+/** PDO entry constructor.
+ */
+void ec_pdo_entry_init(
+ ec_pdo_entry_t *entry /**< PDO entry. */
+ )
+{
+ entry->name = NULL;
+}
+
+/*****************************************************************************/
+
+/** PDO entry copy constructor.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_pdo_entry_init_copy(
+ ec_pdo_entry_t *entry, /**< PDO entry. */
+ const ec_pdo_entry_t *other /**< PDO entry to copy from. */
+ )
+{
+ entry->index = other->index;
+ entry->subindex = other->subindex;
+ entry->name = NULL;
+ entry->bit_length = other->bit_length;
+
+ return ec_pdo_entry_set_name(entry, other->name);
+}
+
+/*****************************************************************************/
+
+/** PDO entry destructor.
+ */
+void ec_pdo_entry_clear(ec_pdo_entry_t *entry /**< PDO entry. */)
+{
+ if (entry->name)
+ kfree(entry->name);
+}
+
+/*****************************************************************************/
+
+/** Set PDO entry name.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_pdo_entry_set_name(
+ ec_pdo_entry_t *entry, /**< PDO entry. */
+ const char *name /**< New name. */
+ )
+{
+ unsigned int len;
+
+ if (entry->name && name && !strcmp(entry->name, name))
+ return 0;
+
+ if (entry->name)
+ kfree(entry->name);
+
+ if (name && (len = strlen(name))) {
+ if (!(entry->name = (char *) kmalloc(len + 1, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate PDO entry name.\n");
+ return -ENOMEM;
+ }
+ memcpy(entry->name, name, len + 1);
+ } else {
+ entry->name = NULL;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Compares two PDO entries.
+ *
+ * \retval 1 The entries are equal.
+ * \retval 0 The entries differ.
+ */
+int ec_pdo_entry_equal(
+ const ec_pdo_entry_t *entry1, /**< First PDO entry. */
+ const ec_pdo_entry_t *entry2 /**< Second PDO entry. */
+ )
+{
+ return entry1->index == entry2->index
+ && entry1->subindex == entry2->subindex
+ && entry1->bit_length == entry2->bit_length;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/pdo_entry.h b/net/ethercat/master/pdo_entry.h
new file mode 100644
index 000000000000..f78e4d2533bf
--- /dev/null
+++ b/net/ethercat/master/pdo_entry.h
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 Process data object structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_PDO_ENTRY_H__
+#define __EC_PDO_ENTRY_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** PDO entry description.
+ */
+typedef struct {
+ struct list_head list; /**< list item */
+ uint16_t index; /**< PDO entry index */
+ uint8_t subindex; /**< PDO entry subindex */
+ char *name; /**< entry name */
+ uint8_t bit_length; /**< entry length in bit */
+} ec_pdo_entry_t;
+
+/*****************************************************************************/
+
+void ec_pdo_entry_init(ec_pdo_entry_t *);
+int ec_pdo_entry_init_copy(ec_pdo_entry_t *, const ec_pdo_entry_t *);
+void ec_pdo_entry_clear(ec_pdo_entry_t *);
+int ec_pdo_entry_set_name(ec_pdo_entry_t *, const char *);
+int ec_pdo_entry_equal(const ec_pdo_entry_t *, const ec_pdo_entry_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/pdo_list.c b/net/ethercat/master/pdo_list.c
new file mode 100644
index 000000000000..dbff60527996
--- /dev/null
+++ b/net/ethercat/master/pdo_list.c
@@ -0,0 +1,346 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO list methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include "globals.h"
+#include "pdo.h"
+#include "slave_config.h"
+#include "master.h"
+
+#include "pdo_list.h"
+
+/*****************************************************************************/
+
+/** PDO list constructor.
+ */
+void ec_pdo_list_init(
+ ec_pdo_list_t *pl /**< PDO list. */
+ )
+{
+ INIT_LIST_HEAD(&pl->list);
+}
+
+/*****************************************************************************/
+
+/** PDO list destructor.
+ */
+void ec_pdo_list_clear(ec_pdo_list_t *pl /**< PDO list. */)
+{
+ ec_pdo_list_clear_pdos(pl);
+}
+
+/*****************************************************************************/
+
+/** Clears the list of mapped PDOs.
+ */
+void ec_pdo_list_clear_pdos(ec_pdo_list_t *pl /**< PDO list. */)
+{
+ ec_pdo_t *pdo, *next;
+
+ list_for_each_entry_safe(pdo, next, &pl->list, list) {
+ list_del_init(&pdo->list);
+ ec_pdo_clear(pdo);
+ kfree(pdo);
+ }
+}
+
+/*****************************************************************************/
+
+/** Calculates the total size of the mapped PDO entries.
+ *
+ * \retval Data size in byte.
+ */
+uint16_t ec_pdo_list_total_size(
+ const ec_pdo_list_t *pl /**< PDO list. */
+ )
+{
+ unsigned int bit_size;
+ const ec_pdo_t *pdo;
+ const ec_pdo_entry_t *pdo_entry;
+ uint16_t byte_size;
+
+ bit_size = 0;
+ list_for_each_entry(pdo, &pl->list, list) {
+ list_for_each_entry(pdo_entry, &pdo->entries, list) {
+ bit_size += pdo_entry->bit_length;
+ }
+ }
+
+ if (bit_size % 8) // round up to full bytes
+ byte_size = bit_size / 8 + 1;
+ else
+ byte_size = bit_size / 8;
+
+ return byte_size;
+}
+
+/*****************************************************************************/
+
+/** Add a new PDO to the list.
+ *
+ * \return Pointer to new PDO, otherwise an ERR_PTR() code.
+ */
+ec_pdo_t *ec_pdo_list_add_pdo(
+ ec_pdo_list_t *pl, /**< PDO list. */
+ uint16_t index /**< PDO index. */
+ )
+{
+ ec_pdo_t *pdo;
+
+ if (!(pdo = (ec_pdo_t *) kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
+ EC_ERR("Failed to allocate memory for PDO.\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ec_pdo_init(pdo);
+ pdo->index = index;
+ list_add_tail(&pdo->list, &pl->list);
+ return pdo;
+}
+
+/*****************************************************************************/
+
+/** Add the copy of an existing PDO to the list.
+ *
+ * \return 0 on success, else < 0
+ */
+int ec_pdo_list_add_pdo_copy(
+ ec_pdo_list_t *pl, /**< PDO list. */
+ const ec_pdo_t *pdo /**< PDO to add. */
+ )
+{
+ ec_pdo_t *mapped_pdo;
+ int ret;
+
+ // PDO already mapped?
+ list_for_each_entry(mapped_pdo, &pl->list, list) {
+ if (mapped_pdo->index != pdo->index) continue;
+ EC_ERR("PDO 0x%04X is already mapped!\n", pdo->index);
+ return -EEXIST;
+ }
+
+ if (!(mapped_pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
+ EC_ERR("Failed to allocate PDO memory.\n");
+ return -ENOMEM;
+ }
+
+ ret = ec_pdo_init_copy(mapped_pdo, pdo);
+ if (ret < 0) {
+ kfree(mapped_pdo);
+ return ret;
+ }
+
+ list_add_tail(&mapped_pdo->list, &pl->list);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Makes a deep copy of another PDO list.
+ *
+ * \return 0 on success, else < 0
+ */
+int ec_pdo_list_copy(
+ ec_pdo_list_t *pl, /**< PDO list. */
+ const ec_pdo_list_t *other /**< PDO list to copy from. */
+ )
+{
+ ec_pdo_t *other_pdo;
+ int ret;
+
+ ec_pdo_list_clear_pdos(pl);
+
+ // PDO already mapped?
+ list_for_each_entry(other_pdo, &other->list, list) {
+ ret = ec_pdo_list_add_pdo_copy(pl, other_pdo);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Compares two PDO lists.
+ *
+ * Only the list is compared, not the PDO entries (i. e. the PDO
+ * mapping).
+ *
+ * \retval 1 The given PDO lists are equal.
+ * \retval 0 The given PDO lists differ.
+ */
+int ec_pdo_list_equal(
+ const ec_pdo_list_t *pl1, /**< First list. */
+ const ec_pdo_list_t *pl2 /**< Second list. */
+ )
+{
+ const struct list_head *h1, *h2, *l1, *l2;
+ const ec_pdo_t *p1, *p2;
+
+ h1 = l1 = &pl1->list;
+ h2 = l2 = &pl2->list;
+
+ while (1) {
+ l1 = l1->next;
+ l2 = l2->next;
+
+ if ((l1 == h1) ^ (l2 == h2)) // unequal lengths
+ return 0;
+ if (l1 == h1) // both finished
+ break;
+
+ p1 = list_entry(l1, ec_pdo_t, list);
+ p2 = list_entry(l2, ec_pdo_t, list);
+
+ if (p1->index != p2->index)
+ return 0;
+ }
+
+ return 1;
+}
+
+/*****************************************************************************/
+
+/** Finds a PDO with the given index.
+ *
+ * \return Search result, or NULL.
+ */
+ec_pdo_t *ec_pdo_list_find_pdo(
+ const ec_pdo_list_t *pl, /**< PDO list. */
+ uint16_t index /**< PDO index. */
+ )
+{
+ ec_pdo_t *pdo;
+
+ list_for_each_entry(pdo, &pl->list, list) {
+ if (pdo->index != index)
+ continue;
+ return pdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Finds a PDO with the given index and returns a const pointer.
+ *
+ * \return Search result, or NULL.
+ */
+const ec_pdo_t *ec_pdo_list_find_pdo_const(
+ const ec_pdo_list_t *pl, /**< PDO list. */
+ uint16_t index /**< PDO index. */
+ )
+{
+ const ec_pdo_t *pdo;
+
+ list_for_each_entry(pdo, &pl->list, list) {
+ if (pdo->index != index)
+ continue;
+ return pdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Finds a PDO via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+const ec_pdo_t *ec_pdo_list_find_pdo_by_pos_const(
+ const ec_pdo_list_t *pl, /**< PDO list. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ const ec_pdo_t *pdo;
+
+ list_for_each_entry(pdo, &pl->list, list) {
+ if (pos--)
+ continue;
+ return pdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Get the number of PDOs in the list.
+ *
+ * \return Number of PDOs.
+ */
+unsigned int ec_pdo_list_count(
+ const ec_pdo_list_t *pl /**< PDO list. */
+ )
+{
+ const ec_pdo_t *pdo;
+ unsigned int num = 0;
+
+ list_for_each_entry(pdo, &pl->list, list) {
+ num++;
+ }
+
+ return num;
+}
+
+/*****************************************************************************/
+
+/** Outputs the PDOs in the list.
+ */
+void ec_pdo_list_print(
+ const ec_pdo_list_t *pl /**< PDO list. */
+ )
+{
+ const ec_pdo_t *pdo;
+
+ if (list_empty(&pl->list)) {
+ printk("(none)");
+ } else {
+ list_for_each_entry(pdo, &pl->list, list) {
+ printk("0x%04X", pdo->index);
+ if (pdo->list.next != &pl->list)
+ printk(" ");
+ }
+ }
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/pdo_list.h b/net/ethercat/master/pdo_list.h
new file mode 100644
index 000000000000..e2a78ccbf93a
--- /dev/null
+++ b/net/ethercat/master/pdo_list.h
@@ -0,0 +1,79 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 PDO list structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_PDO_LIST_H__
+#define __EC_PDO_LIST_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "pdo.h"
+
+/*****************************************************************************/
+
+/** EtherCAT PDO list.
+ */
+typedef struct {
+ struct list_head list; /**< List of PDOs. */
+} ec_pdo_list_t;
+
+/*****************************************************************************/
+
+void ec_pdo_list_init(ec_pdo_list_t *);
+void ec_pdo_list_clear(ec_pdo_list_t *);
+
+void ec_pdo_list_clear_pdos(ec_pdo_list_t *);
+
+ec_pdo_t *ec_pdo_list_add_pdo(ec_pdo_list_t *, uint16_t);
+int ec_pdo_list_add_pdo_copy(ec_pdo_list_t *, const ec_pdo_t *);
+
+int ec_pdo_list_copy(ec_pdo_list_t *, const ec_pdo_list_t *);
+
+uint16_t ec_pdo_list_total_size(const ec_pdo_list_t *);
+int ec_pdo_list_equal(const ec_pdo_list_t *, const ec_pdo_list_t *);
+
+ec_pdo_t *ec_pdo_list_find_pdo(const ec_pdo_list_t *, uint16_t);
+const ec_pdo_t *ec_pdo_list_find_pdo_const(const ec_pdo_list_t *,
+ uint16_t);
+const ec_pdo_t *ec_pdo_list_find_pdo_by_pos_const(
+ const ec_pdo_list_t *, unsigned int);
+unsigned int ec_pdo_list_count(const ec_pdo_list_t *);
+
+void ec_pdo_list_print(const ec_pdo_list_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/reg_request.c b/net/ethercat/master/reg_request.c
new file mode 100644
index 000000000000..c1670d6141eb
--- /dev/null
+++ b/net/ethercat/master/reg_request.c
@@ -0,0 +1,131 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2012 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
+ * Register request functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "reg_request.h"
+
+/*****************************************************************************/
+
+/** Register request constructor.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_reg_request_init(
+ ec_reg_request_t *reg, /**< Register request. */
+ size_t size /**< Memory size. */
+ )
+{
+ if (!(reg->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate %zu bytes of register memory.\n", size);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&reg->list);
+ reg->mem_size = size;
+ memset(reg->data, 0x00, size);
+ reg->dir = EC_DIR_INVALID;
+ reg->address = 0;
+ reg->transfer_size = 0;
+ reg->state = EC_INT_REQUEST_INIT;
+ reg->ring_position = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Register request destructor.
+ */
+void ec_reg_request_clear(
+ ec_reg_request_t *reg /**< Register request. */
+ )
+{
+ if (reg->data) {
+ kfree(reg->data);
+ }
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+uint8_t *ecrt_reg_request_data(ec_reg_request_t *reg)
+{
+ return reg->data;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_reg_request_state(const ec_reg_request_t *reg)
+{
+ return ec_request_state_translation_table[reg->state];
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_write(ec_reg_request_t *reg, uint16_t address,
+ size_t size)
+{
+ reg->dir = EC_DIR_OUTPUT;
+ reg->address = address;
+ reg->transfer_size = min(size, reg->mem_size);
+ reg->state = EC_INT_REQUEST_QUEUED;
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_read(ec_reg_request_t *reg, uint16_t address,
+ size_t size)
+{
+ reg->dir = EC_DIR_INPUT;
+ reg->address = address;
+ reg->transfer_size = min(size, reg->mem_size);
+ reg->state = EC_INT_REQUEST_QUEUED;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_reg_request_data);
+EXPORT_SYMBOL(ecrt_reg_request_state);
+EXPORT_SYMBOL(ecrt_reg_request_write);
+EXPORT_SYMBOL(ecrt_reg_request_read);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/reg_request.h b/net/ethercat/master/reg_request.h
new file mode 100644
index 000000000000..f12212925885
--- /dev/null
+++ b/net/ethercat/master/reg_request.h
@@ -0,0 +1,67 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2012 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 register request structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_REG_REQUEST_H__
+#define __EC_REG_REQUEST_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** Register request.
+ */
+struct ec_reg_request {
+ struct list_head list; /**< List item. */
+ size_t mem_size; /**< Size of data memory. */
+ uint8_t *data; /**< Pointer to data memory. */
+ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means writing to the
+ slave, EC_DIR_INPUT means reading from the slave. */
+ uint16_t address; /**< Register address. */
+ size_t transfer_size; /**< Size of the data to transfer. */
+ ec_internal_request_state_t state; /**< Request state. */
+ uint16_t ring_position; /**< Ring position for emergency requests. */
+};
+
+/*****************************************************************************/
+
+int ec_reg_request_init(ec_reg_request_t *, size_t);
+void ec_reg_request_clear(ec_reg_request_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/rtdm-ioctl.c b/net/ethercat/master/rtdm-ioctl.c
new file mode 100644
index 000000000000..69fb35527a8a
--- /dev/null
+++ b/net/ethercat/master/rtdm-ioctl.c
@@ -0,0 +1,4646 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 master character device.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/vmalloc.h>
+
+#include "master.h"
+#include "slave_config.h"
+#include "voe_handler.h"
+#include "ethernet.h"
+#include "ioctl.h"
+
+/** Set to 1 to enable ioctl() latency tracing.
+ *
+ * Requires CPU timestamp counter!
+ */
+#define DEBUG_LATENCY 0
+
+/** Optional compiler attributes fo ioctl() functions.
+ */
+#if 0
+#define ATTRIBUTES __attribute__ ((__noinline__))
+#else
+#define ATTRIBUTES
+#endif
+
+/*****************************************************************************/
+
+/** Copies a string to an ioctl structure.
+ */
+static void ec_ioctl_strcpy(
+ char *target, /**< Target. */
+ const char *source /**< Source. */
+ )
+{
+ if (source) {
+ strncpy(target, source, EC_IOCTL_STRING_SIZE);
+ target[EC_IOCTL_STRING_SIZE - 1] = 0;
+ } else {
+ target[0] = 0;
+ }
+}
+
+/*****************************************************************************/
+
+/** Get module information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_module(
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_module_t data;
+
+ data.ioctl_version_magic = EC_IOCTL_VERSION_MAGIC;
+ data.master_count = ec_master_count();
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get master information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_master_t io;
+ unsigned int dev_idx, j;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ io.slave_count = master->slave_count;
+ io.config_count = ec_master_config_count(master);
+ io.domain_count = ec_master_domain_count(master);
+#ifdef EC_EOE
+ io.eoe_handler_count = ec_master_eoe_handler_count(master);
+#endif
+ io.phase = (uint8_t) master->phase;
+ io.active = (uint8_t) master->active;
+ io.scan_busy = master->scan_busy;
+
+ up(&master->master_sem);
+
+ if (down_interruptible(&master->device_sem)) {
+ return -EINTR;
+ }
+
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(master); dev_idx++) {
+ ec_device_t *device = &master->devices[dev_idx];
+
+ if (device->dev) {
+ memcpy(io.devices[dev_idx].address, device->dev->dev_addr,
+ ETH_ALEN);
+ } else {
+ memcpy(io.devices[dev_idx].address, master->macs[dev_idx],
+ ETH_ALEN);
+ }
+ io.devices[dev_idx].attached = device->dev ? 1 : 0;
+ io.devices[dev_idx].link_state = device->link_state ? 1 : 0;
+ io.devices[dev_idx].tx_count = device->tx_count;
+ io.devices[dev_idx].rx_count = device->rx_count;
+ io.devices[dev_idx].tx_bytes = device->tx_bytes;
+ io.devices[dev_idx].rx_bytes = device->rx_bytes;
+ io.devices[dev_idx].tx_errors = device->tx_errors;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ io.devices[dev_idx].tx_frame_rates[j] =
+ device->tx_frame_rates[j];
+ io.devices[dev_idx].rx_frame_rates[j] =
+ device->rx_frame_rates[j];
+ io.devices[dev_idx].tx_byte_rates[j] =
+ device->tx_byte_rates[j];
+ io.devices[dev_idx].rx_byte_rates[j] =
+ device->rx_byte_rates[j];
+ }
+ }
+ io.num_devices = ec_master_num_devices(master);
+
+ io.tx_count = master->device_stats.tx_count;
+ io.rx_count = master->device_stats.rx_count;
+ io.tx_bytes = master->device_stats.tx_bytes;
+ io.rx_bytes = master->device_stats.rx_bytes;
+ for (j = 0; j < EC_RATE_COUNT; j++) {
+ io.tx_frame_rates[j] =
+ master->device_stats.tx_frame_rates[j];
+ io.rx_frame_rates[j] =
+ master->device_stats.rx_frame_rates[j];
+ io.tx_byte_rates[j] =
+ master->device_stats.tx_byte_rates[j];
+ io.rx_byte_rates[j] =
+ master->device_stats.rx_byte_rates[j];
+ io.loss_rates[j] =
+ master->device_stats.loss_rates[j];
+ }
+
+ up(&master->device_sem);
+
+ io.app_time = master->app_time;
+ io.dc_ref_time = master->dc_ref_time;
+ io.ref_clock =
+ master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_t data;
+ const ec_slave_t *slave;
+ int i;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n", data.position);
+ return -EINVAL;
+ }
+
+ data.device_index = slave->device_index;
+ data.vendor_id = slave->sii.vendor_id;
+ data.product_code = slave->sii.product_code;
+ data.revision_number = slave->sii.revision_number;
+ data.serial_number = slave->sii.serial_number;
+ data.alias = slave->effective_alias;
+ data.boot_rx_mailbox_offset = slave->sii.boot_rx_mailbox_offset;
+ data.boot_rx_mailbox_size = slave->sii.boot_rx_mailbox_size;
+ data.boot_tx_mailbox_offset = slave->sii.boot_tx_mailbox_offset;
+ data.boot_tx_mailbox_size = slave->sii.boot_tx_mailbox_size;
+ data.std_rx_mailbox_offset = slave->sii.std_rx_mailbox_offset;
+ data.std_rx_mailbox_size = slave->sii.std_rx_mailbox_size;
+ data.std_tx_mailbox_offset = slave->sii.std_tx_mailbox_offset;
+ data.std_tx_mailbox_size = slave->sii.std_tx_mailbox_size;
+ data.mailbox_protocols = slave->sii.mailbox_protocols;
+ data.has_general_category = slave->sii.has_general;
+ data.coe_details = slave->sii.coe_details;
+ data.general_flags = slave->sii.general_flags;
+ data.current_on_ebus = slave->sii.current_on_ebus;
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ data.ports[i].desc = slave->ports[i].desc;
+ data.ports[i].link.link_up = slave->ports[i].link.link_up;
+ data.ports[i].link.loop_closed = slave->ports[i].link.loop_closed;
+ data.ports[i].link.signal_detected =
+ slave->ports[i].link.signal_detected;
+ data.ports[i].receive_time = slave->ports[i].receive_time;
+ if (slave->ports[i].next_slave) {
+ data.ports[i].next_slave =
+ slave->ports[i].next_slave->ring_position;
+ } else {
+ data.ports[i].next_slave = 0xffff;
+ }
+ data.ports[i].delay_to_next_dc = slave->ports[i].delay_to_next_dc;
+ }
+ data.fmmu_bit = slave->base_fmmu_bit_operation;
+ data.dc_supported = slave->base_dc_supported;
+ data.dc_range = slave->base_dc_range;
+ data.has_dc_system_time = slave->has_dc_system_time;
+ data.transmission_delay = slave->transmission_delay;
+ data.al_state = slave->current_state;
+ data.error_flag = slave->error_flag;
+
+ data.sync_count = slave->sii.sync_count;
+ data.sdo_count = ec_slave_sdo_count(slave);
+ data.sii_nwords = slave->sii_nwords;
+ ec_ioctl_strcpy(data.group, slave->sii.group);
+ ec_ioctl_strcpy(data.image, slave->sii.image);
+ ec_ioctl_strcpy(data.order, slave->sii.order);
+ ec_ioctl_strcpy(data.name, slave->sii.name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+
+ data.physical_start_address = sync->physical_start_address;
+ data.default_size = sync->default_length;
+ data.control_register = sync->control_register;
+ data.enable = sync->enable;
+ data.pdo_count = ec_pdo_list_count(&sync->pdos);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager PDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_pdo_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+ const ec_pdo_t *pdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sync->pdos, data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
+ "position %u!\n", data.sync_index, data.pdo_pos);
+ return -EINVAL;
+ }
+
+ data.index = pdo->index;
+ data.entry_count = ec_pdo_entry_count(pdo);
+ ec_ioctl_strcpy(data.name, pdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave sync manager PDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sync_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_slave_sync_pdo_entry_t data;
+ const ec_slave_t *slave;
+ const ec_sync_t *sync;
+ const ec_pdo_t *pdo;
+ const ec_pdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sync_index >= slave->sii.sync_count) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not exist!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ sync = &slave->sii.syncs[data.sync_index];
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sync->pdos, data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Sync manager %u does not contain a PDO with "
+ "position %u!\n", data.sync_index, data.pdo_pos);
+ return -EINVAL;
+ }
+
+ if (!(entry = ec_pdo_find_entry_by_pos_const(
+ pdo, data.entry_pos))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "PDO 0x%04X does not contain an entry with "
+ "position %u!\n", data.pdo_pos, data.entry_pos);
+ return -EINVAL;
+ }
+
+ data.index = entry->index;
+ data.subindex = entry->subindex;
+ data.bit_length = entry->bit_length;
+ ec_ioctl_strcpy(data.name, entry->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_t data;
+ const ec_domain_t *domain;
+ unsigned int dev_idx;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n", data.index);
+ return -EINVAL;
+ }
+
+ data.data_size = domain->data_size;
+ data.logical_base_address = domain->logical_base_address;
+ for (dev_idx = EC_DEVICE_MAIN;
+ dev_idx < ec_master_num_devices(domain->master); dev_idx++) {
+ data.working_counter[dev_idx] = domain->working_counter[dev_idx];
+ }
+ data.expected_working_counter = domain->expected_working_counter;
+ data.fmmu_count = ec_domain_fmmu_count(domain);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain FMMU information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_fmmu(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_fmmu_t data;
+ const ec_domain_t *domain;
+ const ec_fmmu_config_t *fmmu;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n",
+ data.domain_index);
+ return -EINVAL;
+ }
+
+ if (!(fmmu = ec_domain_find_fmmu(domain, data.fmmu_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u has less than %u"
+ " fmmu configurations.\n",
+ data.domain_index, data.fmmu_index + 1);
+ return -EINVAL;
+ }
+
+ data.slave_config_alias = fmmu->sc->alias;
+ data.slave_config_position = fmmu->sc->position;
+ data.sync_index = fmmu->sync_index;
+ data.dir = fmmu->dir;
+ data.logical_address = fmmu->logical_start_address;
+ data.data_size = fmmu->data_size;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get domain data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< Userspace address to store the results. */
+ )
+{
+ ec_ioctl_domain_data_t data;
+ const ec_domain_t *domain;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Domain %u does not exist!\n",
+ data.domain_index);
+ return -EINVAL;
+ }
+
+ if (domain->data_size != data.data_size) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Data size mismatch %u/%zu!\n",
+ data.data_size, domain->data_size);
+ return -EFAULT;
+ }
+
+ if (copy_to_user((void __user *) data.target, domain->data,
+ domain->data_size)) {
+ up(&master->master_sem);
+ return -EFAULT;
+ }
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set master debug level.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_debug(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ return ec_master_debug_level(master, (unsigned long) arg);
+}
+
+/*****************************************************************************/
+
+/** Issue a bus scan.
+ *
+ * \return Always zero (success).
+ */
+static ATTRIBUTES int ec_ioctl_master_rescan(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ master->fsm.rescan_required = 1;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set slave state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_state_t data;
+ ec_slave_t *slave;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ ec_slave_request_state(slave, data.al_state);
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave SDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_t data;
+ const ec_slave_t *slave;
+ const ec_sdo_t *sdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (!(sdo = ec_slave_get_sdo_by_pos_const(
+ slave, data.sdo_position))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", data.sdo_position);
+ return -EINVAL;
+ }
+
+ data.sdo_index = sdo->index;
+ data.max_subindex = sdo->max_subindex;
+ ec_ioctl_strcpy(data.name, sdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave SDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_entry_t data;
+ const ec_slave_t *slave;
+ const ec_sdo_t *sdo;
+ const ec_sdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (data.sdo_spec <= 0) {
+ if (!(sdo = ec_slave_get_sdo_by_pos_const(
+ slave, -data.sdo_spec))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO %u does not exist!\n", -data.sdo_spec);
+ return -EINVAL;
+ }
+ } else {
+ if (!(sdo = ec_slave_get_sdo_const(
+ slave, data.sdo_spec))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO 0x%04X does not exist!\n",
+ data.sdo_spec);
+ return -EINVAL;
+ }
+ }
+
+ if (!(entry = ec_sdo_get_entry_const(
+ sdo, data.sdo_entry_subindex))) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "SDO entry 0x%04X:%02X does not exist!\n",
+ sdo->index, data.sdo_entry_subindex);
+ return -EINVAL;
+ }
+
+ data.data_type = entry->data_type;
+ data.bit_length = entry->bit_length;
+ data.read_access[EC_SDO_ENTRY_ACCESS_PREOP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP];
+ data.read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
+ data.read_access[EC_SDO_ENTRY_ACCESS_OP] =
+ entry->read_access[EC_SDO_ENTRY_ACCESS_OP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_PREOP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP];
+ data.write_access[EC_SDO_ENTRY_ACCESS_OP] =
+ entry->write_access[EC_SDO_ENTRY_ACCESS_OP];
+ ec_ioctl_strcpy(data.description, entry->description);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Upload SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_upload(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_upload_t data;
+ uint8_t *target;
+ int ret;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!(target = kmalloc(data.target_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes"
+ " for SDO upload.\n", data.target_size);
+ return -ENOMEM;
+ }
+
+ ret = ecrt_master_sdo_upload(master, data.slave_position,
+ data.sdo_index, data.sdo_entry_subindex, target,
+ data.target_size, &data.data_size, &data.abort_code);
+
+ if (!ret) {
+ if (copy_to_user((void __user *) data.target,
+ target, data.data_size)) {
+ kfree(target);
+ return -EFAULT;
+ }
+ }
+
+ kfree(target);
+
+ if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Download SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sdo_download(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sdo_download_t data;
+ uint8_t *sdo_data;
+ int retval;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!(sdo_data = kmalloc(data.data_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes"
+ " for SDO download.\n", data.data_size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(sdo_data, (void __user *) data.data, data.data_size)) {
+ kfree(sdo_data);
+ return -EFAULT;
+ }
+
+ if (data.complete_access) {
+ retval = ecrt_master_sdo_download_complete(master, data.slave_position,
+ data.sdo_index, sdo_data, data.data_size, &data.abort_code);
+ } else {
+ retval = ecrt_master_sdo_download(master, data.slave_position,
+ data.sdo_index, data.sdo_entry_subindex, sdo_data,
+ data.data_size, &data.abort_code);
+ }
+
+ kfree(sdo_data);
+
+ if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+ retval = -EFAULT;
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Read a slave's SII.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sii_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sii_t data;
+ const ec_slave_t *slave;
+ int retval;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(slave = ec_master_find_slave_const(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ return -EINVAL;
+ }
+
+ if (!data.nwords
+ || data.offset + data.nwords > slave->sii_nwords) {
+ up(&master->master_sem);
+ EC_SLAVE_ERR(slave, "Invalid SII read offset/size %u/%u for slave SII"
+ " size %zu!\n", data.offset, data.nwords, slave->sii_nwords);
+ return -EINVAL;
+ }
+
+ if (copy_to_user((void __user *) data.words,
+ slave->sii_words + data.offset, data.nwords * 2))
+ retval = -EFAULT;
+ else
+ retval = 0;
+
+ up(&master->master_sem);
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's SII.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_sii_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_sii_t data;
+ ec_slave_t *slave;
+ unsigned int byte_size;
+ uint16_t *words;
+ ec_sii_write_request_t request;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (!data.nwords) {
+ return 0;
+ }
+
+ byte_size = sizeof(uint16_t) * data.nwords;
+ if (!(words = kmalloc(byte_size, GFP_KERNEL))) {
+ EC_MASTER_ERR(master, "Failed to allocate %u bytes"
+ " for SII contents.\n", byte_size);
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(words,
+ (void __user *) data.words, byte_size)) {
+ kfree(words);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(words);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, data.slave_position))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ data.slave_position);
+ kfree(words);
+ return -EINVAL;
+ }
+
+ // init SII write request
+ INIT_LIST_HEAD(&request.list);
+ request.slave = slave;
+ request.words = words;
+ request.offset = data.offset;
+ request.nwords = data.nwords;
+ request.state = EC_INT_REQUEST_QUEUED;
+
+ // schedule SII write request.
+ list_add_tail(&request.list, &master->sii_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ kfree(words);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ kfree(words);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Read a slave's registers.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_reg_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_reg_t io;
+ ec_slave_t *slave;
+ ec_reg_request_t request;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (!io.size) {
+ return 0;
+ }
+
+ // init register request
+ ret = ec_reg_request_init(&request, io.size);
+ if (ret) {
+ return ret;
+ }
+
+ ecrt_reg_request_read(&request, io.address, io.size);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(
+ master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->reg_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ if (request.state == EC_INT_REQUEST_SUCCESS) {
+ if (copy_to_user((void __user *) io.data, request.data, io.size)) {
+ return -EFAULT;
+ }
+ }
+ ec_reg_request_clear(&request);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's registers.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_reg_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_reg_t io;
+ ec_slave_t *slave;
+ ec_reg_request_t request;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (!io.size) {
+ return 0;
+ }
+
+ // init register request
+ ret = ec_reg_request_init(&request, io.size);
+ if (ret) {
+ return ret;
+ }
+
+ if (copy_from_user(request.data, (void __user *) io.data, io.size)) {
+ ec_reg_request_clear(&request);
+ return -EFAULT;
+ }
+
+ ecrt_reg_request_write(&request, io.address, io.size);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (io.emergency) {
+ request.ring_position = io.slave_position;
+ // schedule request.
+ list_add_tail(&request.list, &master->emerg_reg_requests);
+ }
+ else {
+ if (!(slave = ec_master_find_slave(master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->reg_requests);
+ }
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_reg_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ ec_reg_request_clear(&request);
+
+ return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_t data;
+ const ec_slave_config_t *sc;
+ uint8_t i;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ data.alias = sc->alias;
+ data.position = sc->position;
+ data.vendor_id = sc->vendor_id;
+ data.product_code = sc->product_code;
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) {
+ data.syncs[i].dir = sc->sync_configs[i].dir;
+ data.syncs[i].watchdog_mode = sc->sync_configs[i].watchdog_mode;
+ data.syncs[i].pdo_count =
+ ec_pdo_list_count(&sc->sync_configs[i].pdos);
+ }
+ data.watchdog_divider = sc->watchdog_divider;
+ data.watchdog_intervals = sc->watchdog_intervals;
+ data.sdo_count = ec_slave_config_sdo_count(sc);
+ data.idn_count = ec_slave_config_idn_count(sc);
+ data.slave_position = sc->slave ? sc->slave->ring_position : -1;
+ data.dc_assign_activate = sc->dc_assign_activate;
+ for (i = 0; i < EC_SYNC_SIGNAL_COUNT; i++) {
+ data.dc_sync[i] = sc->dc_sync[i];
+ }
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration PDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ const ec_slave_config_t *sc;
+ const ec_pdo_t *pdo;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (data.sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_MASTER_ERR(master, "Invalid sync manager index %u!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sc->sync_configs[data.sync_index].pdos,
+ data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid PDO position!\n");
+ return -EINVAL;
+ }
+
+ data.index = pdo->index;
+ data.entry_count = ec_pdo_entry_count(pdo);
+ ec_ioctl_strcpy(data.name, pdo->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration PDO entry information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_pdo_entry_t data;
+ const ec_slave_config_t *sc;
+ const ec_pdo_t *pdo;
+ const ec_pdo_entry_t *entry;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (data.sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_MASTER_ERR(master, "Invalid sync manager index %u!\n",
+ data.sync_index);
+ return -EINVAL;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config_const(
+ master, data.config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ data.config_index);
+ return -EINVAL;
+ }
+
+ if (!(pdo = ec_pdo_list_find_pdo_by_pos_const(
+ &sc->sync_configs[data.sync_index].pdos,
+ data.pdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid PDO position!\n");
+ return -EINVAL;
+ }
+
+ if (!(entry = ec_pdo_find_entry_by_pos_const(
+ pdo, data.entry_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Entry not found!\n");
+ return -EINVAL;
+ }
+
+ data.index = entry->index;
+ data.subindex = entry->subindex;
+ data.bit_length = entry->bit_length;
+ ec_ioctl_strcpy(data.name, entry->name);
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration SDO information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_sdo_t *ioctl;
+ const ec_slave_config_t *sc;
+ const ec_sdo_request_t *req;
+
+ if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(ioctl);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config_const(
+ master, ioctl->config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ ioctl->config_index);
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ if (!(req = ec_slave_config_get_sdo_by_pos_const(
+ sc, ioctl->sdo_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid SDO position!\n");
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ ioctl->index = req->index;
+ ioctl->subindex = req->subindex;
+ ioctl->size = req->data_size;
+ memcpy(ioctl->data, req->data,
+ min((u32) ioctl->size, (u32) EC_MAX_SDO_DATA_SIZE));
+ ioctl->complete_access = req->complete_access;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ kfree(ioctl);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration IDN information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_config_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_config_idn_t *ioctl;
+ const ec_slave_config_t *sc;
+ const ec_soe_request_t *req;
+
+ if (!(ioctl = kmalloc(sizeof(*ioctl), GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(ioctl, (void __user *) arg, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(ioctl);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config_const(
+ master, ioctl->config_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Slave config %u does not exist!\n",
+ ioctl->config_index);
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ if (!(req = ec_slave_config_get_idn_by_pos_const(
+ sc, ioctl->idn_pos))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "Invalid IDN position!\n");
+ kfree(ioctl);
+ return -EINVAL;
+ }
+
+ ioctl->drive_no = req->drive_no;
+ ioctl->idn = req->idn;
+ ioctl->state = req->state;
+ ioctl->size = req->data_size;
+ memcpy(ioctl->data, req->data,
+ min((u32) ioctl->size, (u32) EC_MAX_IDN_DATA_SIZE));
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, ioctl, sizeof(*ioctl))) {
+ kfree(ioctl);
+ return -EFAULT;
+ }
+
+ kfree(ioctl);
+ return 0;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_EOE
+
+/** Get EoE handler information.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_eoe_handler(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_eoe_handler_t data;
+ const ec_eoe_t *eoe;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(eoe = ec_master_get_eoe_handler_const(master, data.eoe_index))) {
+ up(&master->master_sem);
+ EC_MASTER_ERR(master, "EoE handler %u does not exist!\n",
+ data.eoe_index);
+ return -EINVAL;
+ }
+
+ if (eoe->slave) {
+ data.slave_position = eoe->slave->ring_position;
+ } else {
+ data.slave_position = 0xffff;
+ }
+ snprintf(data.name, EC_DATAGRAM_NAME_SIZE, eoe->dev->name);
+ data.open = eoe->opened;
+ data.rx_bytes = eoe->stats.tx_bytes;
+ data.rx_rate = eoe->tx_rate;
+ data.tx_bytes = eoe->stats.rx_bytes;
+ data.tx_rate = eoe->tx_rate;
+ data.tx_queued_frames = eoe->tx_queued_frames;
+ data.tx_queue_size = eoe->tx_queue_size;
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+#endif
+
+/*****************************************************************************/
+
+/** Request the master from userspace.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_master_t *m;
+ int ret = 0;
+
+ m = ecrt_request_master_err(master->index);
+ if (IS_ERR(m)) {
+ ret = PTR_ERR(m);
+ } else {
+ ctx->requested = 1;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Create a domain.
+ *
+ * \return Domain index on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_create_domain(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ domain = ecrt_master_create_domain_err(master);
+ if (IS_ERR(domain))
+ return PTR_ERR(domain);
+
+ return domain->index;
+}
+
+/*****************************************************************************/
+
+/** Create a slave configuration.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_create_slave_config(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc, *entry;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ sc = ecrt_master_slave_config_err(master, data.alias, data.position,
+ data.vendor_id, data.product_code);
+ if (IS_ERR(sc))
+ return PTR_ERR(sc);
+
+ data.config_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ list_for_each_entry(entry, &master->configs, list) {
+ if (entry == sc)
+ break;
+ data.config_index++;
+ }
+
+ up(&master->master_sem);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Select the DC reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_select_ref_clock(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ unsigned long config_index = (unsigned long) arg;
+ ec_slave_config_t *sc = NULL;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (config_index != 0xFFFFFFFF) {
+ if (!(sc = ec_master_get_config(master, config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+ }
+
+ ecrt_master_select_reference_clock(master, sc);
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Activates the master.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_activate(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_master_activate_t io;
+ ec_domain_t *domain;
+ off_t offset;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ io.process_data = NULL;
+
+ /* Get the sum of the domains' process data sizes. */
+
+ ctx->process_data_size = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ list_for_each_entry(domain, &master->domains, list) {
+ ctx->process_data_size += ecrt_domain_size(domain);
+ }
+
+ up(&master->master_sem);
+
+ if (ctx->process_data_size) {
+ ctx->process_data = vmalloc(ctx->process_data_size);
+ if (!ctx->process_data) {
+ ctx->process_data_size = 0;
+ return -ENOMEM;
+ }
+
+ /* Set the memory as external process data memory for the
+ * domains.
+ */
+ offset = 0;
+ list_for_each_entry(domain, &master->domains, list) {
+ ecrt_domain_external_memory(domain,
+ ctx->process_data + offset);
+ offset += ecrt_domain_size(domain);
+ }
+
+#ifdef EC_IOCTL_RTDM
+ /* RTDM uses a different approach for memory-mapping, which has to be
+ * initiated by the kernel.
+ */
+ ret = ec_rtdm_mmap(ctx, &io.process_data);
+ if (ret < 0) {
+ EC_MASTER_ERR(master, "Failed to map process data"
+ " memory to user space (code %i).\n", ret);
+ return ret;
+ }
+#endif
+ }
+
+ io.process_data_size = ctx->process_data_size;
+
+#ifndef EC_IOCTL_RTDM
+ ecrt_master_callbacks(master, ec_master_internal_send_cb,
+ ec_master_internal_receive_cb, master);
+#endif
+
+ ret = ecrt_master_activate(master);
+ if (ret < 0)
+ return ret;
+
+ if (copy_to_user((void __user *) arg, &io,
+ sizeof(ec_ioctl_master_activate_t)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Deactivates the master.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_deactivate(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ ecrt_master_deactivate(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set max. number of databytes in a cycle
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_set_send_interval(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ size_t send_interval;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&send_interval, (void __user *) arg,
+ sizeof(send_interval))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ ec_master_set_send_interval(master, send_interval);
+
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Send frames.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_send(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_send(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Receive frames.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_receive(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_receive(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the master state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_master_state_t data;
+
+ ecrt_master_state(master, &data);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the link state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_master_link_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_link_state_t ioctl;
+ ec_master_link_state_t state;
+ int ret;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ ret = ecrt_master_link_state(master, ioctl.dev_idx, &state);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) ioctl.state, &state, sizeof(state))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Set the master DC application time.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_app_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint64_t time;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&time, (void __user *) arg, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ ecrt_master_application_time(master, time);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_ref(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_reference_clock(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_ref_to(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint64_t time;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&time, (void __user *) arg, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ ecrt_master_sync_reference_clock_to(master, time);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the slave clocks.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_slaves(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_slave_clocks(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the system time of the reference clock.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_ref_clock_time(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint32_t time;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ret = ecrt_master_reference_clock_time(master, &time);
+ if (ret) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) arg, &time, sizeof(time))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the sync monitoring datagram.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_mon_queue(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ ecrt_master_sync_monitor_queue(master);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Processes the sync monitoring datagram.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sync_mon_process(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ uint32_t time_diff;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ time_diff = ecrt_master_sync_monitor_process(master);
+
+ if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Reset configuration.
+ *
+ * \return Always zero (success).
+ */
+static ATTRIBUTES int ec_ioctl_reset(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ down(&master->master_sem);
+ ecrt_master_reset(master);
+ up(&master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configure a sync manager.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_sync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+ unsigned int i;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ ret = -EFAULT;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++) {
+ if (data.syncs[i].config_this) {
+ ret = ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir,
+ data.syncs[i].watchdog_mode);
+ if (ret) {
+ goto out_up;
+ }
+ }
+ }
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Configure a slave's watchdogs.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_watchdog(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+ int ret = 0;
+
+ if (unlikely(!ctx->requested)) {
+ ret = -EPERM;
+ goto out_return;
+ }
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ ret = -EFAULT;
+ goto out_return;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ ret = -EINTR;
+ goto out_return;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ ret = -ENOENT;
+ goto out_up;
+ }
+
+ ecrt_slave_config_watchdog(sc,
+ data.watchdog_divider, data.watchdog_intervals);
+
+out_up:
+ up(&master->master_sem);
+out_return:
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Add a PDO to the assignment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_add_pdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index);
+}
+
+/*****************************************************************************/
+
+/** Clears the PDO assignment.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_clear_pdos(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ecrt_slave_config_pdo_assign_clear(sc, data.sync_index);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Add an entry to a PDO's mapping.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_add_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_add_pdo_entry_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ return ecrt_slave_config_pdo_mapping_add(sc, data.pdo_index,
+ data.entry_index, data.entry_subindex, data.entry_bit_length);
+}
+
+/*****************************************************************************/
+
+/** Clears the mapping of a PDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_clear_entries(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_pdo_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ecrt_slave_config_pdo_mapping_clear(sc, data.index);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Registers a PDO entry.
+ *
+ * \return Process data offset on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_reg_pdo_entry(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_pdo_entry_t data;
+ ec_slave_config_t *sc;
+ ec_domain_t *domain;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ if (!(domain = ec_master_find_domain(master, data.domain_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc or domain could be invalidated */
+
+ ret = ecrt_slave_config_reg_pdo_entry(sc, data.entry_index,
+ data.entry_subindex, domain, &data.bit_position);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Registers a PDO entry by its position.
+ *
+ * \return Process data offset on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_reg_pdo_pos(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_pdo_pos_t io;
+ ec_slave_config_t *sc;
+ ec_domain_t *domain;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ if (!(domain = ec_master_find_domain(master, io.domain_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc or domain could be invalidated */
+
+ ret = ecrt_slave_config_reg_pdo_entry_pos(sc, io.sync_index,
+ io.pdo_pos, io.entry_pos, domain, &io.bit_position);
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io)))
+ return -EFAULT;
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Sets the DC AssignActivate word and the sync signal times.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_dc(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_config_t data;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ ecrt_slave_config_dc(sc, data.dc_assign_activate,
+ data.dc_sync[0].cycle_time,
+ data.dc_sync[0].shift_time,
+ data.dc_sync[1].cycle_time,
+ data.dc_sync[1].shift_time);
+
+ up(&master->master_sem);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configures an SDO.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_sdo(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_sdo_t data;
+ ec_slave_config_t *sc;
+ uint8_t *sdo_data = NULL;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (!data.size)
+ return -EINVAL;
+
+ if (!(sdo_data = kmalloc(data.size, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(sdo_data, (void __user *) data.data, data.size)) {
+ kfree(sdo_data);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(sdo_data);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ up(&master->master_sem);
+ kfree(sdo_data);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ if (data.complete_access) {
+ ret = ecrt_slave_config_complete_sdo(sc,
+ data.index, sdo_data, data.size);
+ } else {
+ ret = ecrt_slave_config_sdo(sc, data.index, data.subindex, sdo_data,
+ data.size);
+ }
+ kfree(sdo_data);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Set the emergency ring buffer size.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_size(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io)))
+ return -EFAULT;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_size(sc, io.size);
+
+ up(&master->master_sem);
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Get an emergency message from the ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_pop(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ u8 msg[EC_COE_EMERGENCY_MSG_SIZE];
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_pop(sc, msg);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (copy_to_user((void __user *) io.target, msg, sizeof(msg))) {
+ return -EFAULT;
+ }
+
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Clear the emergency ring.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_clear(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ return ecrt_slave_config_emerg_clear(sc);
+}
+
+/*****************************************************************************/
+
+/** Get the number of emergency overruns.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_emerg_overruns(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_emerg_t io;
+ ec_slave_config_t *sc;
+ int ret;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because configuration will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_emerg_overruns(sc);
+ if (ret < 0) {
+ return ret;
+ }
+
+ io.overruns = ret;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create an SDO request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_sdo_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ data.request_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ sc = ec_master_get_config(master, data.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(req, &sc->sdo_requests, list) {
+ data.request_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ req = ecrt_slave_config_create_sdo_request_err(sc, data.sdo_index,
+ data.sdo_subindex, data.size);
+ if (IS_ERR(req))
+ return PTR_ERR(req);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create a register request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_reg_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ io.request_index = 0;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ sc = ec_master_get_config(master, io.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(reg, &sc->reg_requests, list) {
+ io.request_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ reg = ecrt_slave_config_create_reg_request_err(sc, io.mem_size);
+ if (IS_ERR(reg)) {
+ return PTR_ERR(reg);
+ }
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Create a VoE handler.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_voe_handler(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ data.voe_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ sc = ec_master_get_config(master, data.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(voe, &sc->voe_handlers, list) {
+ data.voe_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ voe = ecrt_slave_config_create_voe_handler_err(sc, data.size);
+ if (IS_ERR(voe))
+ return PTR_ERR(voe);
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the slave configuration's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_state_t data;
+ const ec_slave_config_t *sc;
+ ec_slave_config_state_t state;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because sc will not be deleted in the
+ * meantime. */
+
+ if (!(sc = ec_master_get_config_const(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_slave_config_state(sc, &state);
+
+ if (copy_to_user((void __user *) data.state, &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Configures an IDN.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_idn(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sc_idn_t ioctl;
+ ec_slave_config_t *sc;
+ uint8_t *data = NULL;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl)))
+ return -EFAULT;
+
+ if (!ioctl.size)
+ return -EINVAL;
+
+ if (!(data = kmalloc(ioctl.size, GFP_KERNEL))) {
+ return -ENOMEM;
+ }
+
+ if (copy_from_user(data, (void __user *) ioctl.data, ioctl.size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ kfree(data);
+ return -EINTR;
+ }
+
+ if (!(sc = ec_master_get_config(master, ioctl.config_index))) {
+ up(&master->master_sem);
+ kfree(data);
+ return -ENOENT;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ ret = ecrt_slave_config_idn(
+ sc, ioctl.drive_no, ioctl.idn, ioctl.al_state, data, ioctl.size);
+ kfree(data);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Gets the domain's data size.
+ *
+ * \return Domain size, or a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_size(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ const ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ list_for_each_entry(domain, &master->domains, list) {
+ if (domain->index == (unsigned long) arg) {
+ size_t size = ecrt_domain_size(domain);
+ up(&master->master_sem);
+ return size;
+ }
+ }
+
+ up(&master->master_sem);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+/** Gets the domain's offset in the total process data.
+ *
+ * \return Domain offset, or a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_offset(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ int offset = 0;
+ const ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (down_interruptible(&master->master_sem)) {
+ return -EINTR;
+ }
+
+ list_for_each_entry(domain, &master->domains, list) {
+ if (domain->index == (unsigned long) arg) {
+ up(&master->master_sem);
+ return offset;
+ }
+ offset += ecrt_domain_size(domain);
+ }
+
+ up(&master->master_sem);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+/** Process the domain.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_process(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain(master, (unsigned long) arg))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_process(domain);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the domain.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_queue(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_domain_t *domain;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain(master, (unsigned long) arg))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_queue(domain);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the domain state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_domain_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_domain_state_t data;
+ const ec_domain_t *domain;
+ ec_domain_state_t state;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because domain will not be deleted in
+ * the meantime. */
+
+ if (!(domain = ec_master_find_domain_const(master, data.domain_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_domain_state(domain, &state);
+
+ if (copy_to_user((void __user *) data.state, &state, sizeof(state)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets an SDO request's SDO index and subindex.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_index(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_index(req, data.sdo_index, data.sdo_subindex);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets an SDO request's timeout.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_timeout(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_timeout(req, data.timeout);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an SDO request's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ data.state = ecrt_sdo_request_state(req);
+ if (data.state == EC_REQUEST_SUCCESS && req->dir == EC_DIR_INPUT)
+ data.size = ecrt_sdo_request_data_size(req);
+ else
+ data.size = 0;
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SDO read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_sdo_request_read(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SDO write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (!data.size) {
+ EC_MASTER_ERR(master, "SDO download: Data size may not be zero!\n");
+ return -EINVAL;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ret = ec_sdo_request_alloc(req, data.size);
+ if (ret)
+ return ret;
+
+ if (copy_from_user(req->data, (void __user *) data.data, data.size))
+ return -EFAULT;
+
+ req->data_size = data.size;
+ ecrt_sdo_request_write(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read SDO data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sdo_request_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_sdo_request_t data;
+ ec_slave_config_t *sc;
+ ec_sdo_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) data.data, ecrt_sdo_request_data(req),
+ ecrt_sdo_request_data_size(req)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read register data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ if (io.mem_size <= 0) {
+ return 0;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) io.data, ecrt_reg_request_data(reg),
+ min(reg->mem_size, io.mem_size))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an register request's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_state(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ io.state = ecrt_reg_request_state(reg);
+ io.new_data = io.state == EC_REQUEST_SUCCESS && reg->dir == EC_DIR_INPUT;
+
+ if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (io.transfer_size > reg->mem_size) {
+ return -EOVERFLOW;
+ }
+
+ if (copy_from_user(reg->data, (void __user *) io.data,
+ io.transfer_size)) {
+ return -EFAULT;
+ }
+
+ ecrt_reg_request_write(reg, io.address, io.transfer_size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_reg_request_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_reg_request_t io;
+ ec_slave_config_t *sc;
+ ec_reg_request_t *reg;
+
+ if (unlikely(!ctx->requested)) {
+ return -EPERM;
+ }
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor reg will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, io.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+ return -ENOENT;
+ }
+
+ if (io.transfer_size > reg->mem_size) {
+ return -EOVERFLOW;
+ }
+
+ ecrt_reg_request_read(reg, io.address, io.transfer_size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets the VoE send header.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_send_header(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+ uint32_t vendor_id;
+ uint16_t vendor_type;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (get_user(vendor_id, data.vendor_id))
+ return -EFAULT;
+
+ if (get_user(vendor_type, data.vendor_type))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_send_header(voe, vendor_id, vendor_type);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets the received VoE header.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_rec_header(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+ uint32_t vendor_id;
+ uint16_t vendor_type;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_received_header(voe, &vendor_id, &vendor_type);
+
+ if (likely(data.vendor_id))
+ if (put_user(vendor_id, data.vendor_id))
+ return -EFAULT;
+
+ if (likely(data.vendor_type))
+ if (put_user(vendor_type, data.vendor_type))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_read(voe);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE read operation without sending a sync message first.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_read_nosync(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_voe_handler_read_nosync(voe);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts a VoE write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ if (data.size) {
+ if (data.size > ec_voe_handler_mem_size(voe))
+ return -EOVERFLOW;
+
+ if (copy_from_user(ecrt_voe_handler_data(voe),
+ (void __user *) data.data, data.size))
+ return -EFAULT;
+ }
+
+ ecrt_voe_handler_write(voe, data.size);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Executes the VoE state machine.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_exec(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ data.state = ecrt_voe_handler_execute(voe);
+ if (data.state == EC_REQUEST_SUCCESS && voe->dir == EC_DIR_INPUT)
+ data.size = ecrt_voe_handler_data_size(voe);
+ else
+ data.size = 0;
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Reads the received VoE data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_voe_data(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_voe_t data;
+ ec_slave_config_t *sc;
+ ec_voe_handler_t *voe;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor voe will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(voe = ec_slave_config_find_voe_handler(sc, data.voe_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) data.data, ecrt_voe_handler_data(voe),
+ ecrt_voe_handler_data_size(voe)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read a file from a slave via FoE.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_foe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_foe_t io;
+ ec_foe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ ec_foe_request_init(&request, io.file_name);
+ ret = ec_foe_request_alloc(&request, 10000); // FIXME
+ if (ret) {
+ ec_foe_request_clear(&request);
+ return ret;
+ }
+
+ ec_foe_request_read(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+
+ if (!(slave = ec_master_find_slave(master, 0, io.slave_position))) {
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+ io.slave_position);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling FoE read request.\n");
+
+ // schedule request.
+ list_add_tail(&request.list, &slave->foe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+ // request already processing: interrupt not possible.
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ io.result = request.result;
+ io.error_code = request.error_code;
+
+ if (request.state != EC_INT_REQUEST_SUCCESS) {
+ io.data_size = 0;
+ ret = -EIO;
+ } else {
+ if (request.data_size > io.buffer_size) {
+ EC_SLAVE_ERR(slave, "%s(): Buffer too small.\n", __func__);
+ ec_foe_request_clear(&request);
+ return -EOVERFLOW;
+ }
+ io.data_size = request.data_size;
+ if (copy_to_user((void __user *) io.buffer,
+ request.buffer, io.data_size)) {
+ ec_foe_request_clear(&request);
+ return -EFAULT;
+ }
+ ret = 0;
+ }
+
+ if (__copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ ret = -EFAULT;
+ }
+
+ ec_foe_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Write a file to a slave via FoE
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_foe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_foe_t io;
+ ec_foe_request_t request;
+ ec_slave_t *slave;
+ int ret;
+
+ if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+ return -EFAULT;
+ }
+
+ ec_foe_request_init(&request, io.file_name);
+
+ ret = ec_foe_request_alloc(&request, io.buffer_size);
+ if (ret) {
+ ec_foe_request_clear(&request);
+ return ret;
+ }
+
+ if (copy_from_user(request.buffer,
+ (void __user *) io.buffer, io.buffer_size)) {
+ ec_foe_request_clear(&request);
+ return -EFAULT;
+ }
+
+ request.data_size = io.buffer_size;
+ ec_foe_request_write(&request);
+
+ if (down_interruptible(&master->master_sem)) {
+ ec_foe_request_clear(&request);
+ 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);
+ ec_foe_request_clear(&request);
+ return -EINVAL;
+ }
+
+ EC_SLAVE_DBG(slave, 1, "Scheduling FoE write request.\n");
+
+ // schedule FoE write request.
+ list_add_tail(&request.list, &slave->foe_requests);
+
+ up(&master->master_sem);
+
+ // wait for processing through FSM
+ if (wait_event_interruptible(master->request_queue,
+ request.state != EC_INT_REQUEST_QUEUED)) {
+ // interrupted by signal
+ down(&master->master_sem);
+ if (request.state == EC_INT_REQUEST_QUEUED) {
+ // abort request
+ list_del(&request.list);
+ up(&master->master_sem);
+ ec_foe_request_clear(&request);
+ return -EINTR;
+ }
+ up(&master->master_sem);
+ }
+
+ // wait until master FSM has finished processing
+ wait_event(master->request_queue, request.state != EC_INT_REQUEST_BUSY);
+
+ io.result = request.result;
+ io.error_code = request.error_code;
+
+ ret = request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+
+ if (__copy_to_user((void __user *) arg, &io, sizeof(io))) {
+ ret = -EFAULT;
+ }
+
+ ec_foe_request_clear(&request);
+ return ret;
+}
+
+/*****************************************************************************/
+
+/** Read an SoE IDN.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_soe_read(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_soe_read_t ioctl;
+ u8 *data;
+ int retval;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ data = kmalloc(ioctl.mem_size, GFP_KERNEL);
+ if (!data) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n",
+ ioctl.mem_size);
+ return -ENOMEM;
+ }
+
+ retval = ecrt_master_read_idn(master, ioctl.slave_position,
+ ioctl.drive_no, ioctl.idn, data, ioctl.mem_size, &ioctl.data_size,
+ &ioctl.error_code);
+ if (retval) {
+ kfree(data);
+ return retval;
+ }
+
+ if (copy_to_user((void __user *) ioctl.data,
+ data, ioctl.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ kfree(data);
+
+ if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
+ retval = -EFAULT;
+ }
+
+ EC_MASTER_DBG(master, 1, "Finished SoE read request.\n");
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** Write an IDN to a slave via SoE.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_slave_soe_write(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+ ec_ioctl_slave_soe_write_t ioctl;
+ u8 *data;
+ int retval;
+
+ if (copy_from_user(&ioctl, (void __user *) arg, sizeof(ioctl))) {
+ return -EFAULT;
+ }
+
+ data = kmalloc(ioctl.data_size, GFP_KERNEL);
+ if (!data) {
+ EC_MASTER_ERR(master, "Failed to allocate %zu bytes of IDN data.\n",
+ ioctl.data_size);
+ return -ENOMEM;
+ }
+ if (copy_from_user(data, (void __user *) ioctl.data, ioctl.data_size)) {
+ kfree(data);
+ return -EFAULT;
+ }
+
+ retval = ecrt_master_write_idn(master, ioctl.slave_position,
+ ioctl.drive_no, ioctl.idn, data, ioctl.data_size,
+ &ioctl.error_code);
+ kfree(data);
+ if (retval) {
+ return retval;
+ }
+
+ if (__copy_to_user((void __user *) arg, &ioctl, sizeof(ioctl))) {
+ retval = -EFAULT;
+ }
+
+ EC_MASTER_DBG(master, 1, "Finished SoE write request.\n");
+ return retval;
+}
+
+/*****************************************************************************/
+
+/** ioctl() function to use.
+ */
+#ifdef EC_IOCTL_RTDM
+#define EC_IOCTL ec_ioctl_rtdm
+#else
+#define EC_IOCTL ec_ioctl
+#endif
+
+/** Called when an ioctl() command is issued.
+ *
+ * \return ioctl() return code.
+ */
+long EC_IOCTL(
+ ec_master_t *master, /**< EtherCAT master. */
+ ec_ioctl_context_t *ctx, /**< Device context. */
+ unsigned int cmd, /**< ioctl() command identifier. */
+ void *arg /**< ioctl() argument. */
+ )
+{
+#if DEBUG_LATENCY
+ cycles_t a = get_cycles(), b;
+ unsigned int t;
+#endif
+ int ret;
+
+ switch (cmd) {
+ case EC_IOCTL_MODULE:
+ ret = ec_ioctl_module(arg);
+ break;
+ case EC_IOCTL_MASTER:
+ ret = ec_ioctl_master(master, arg);
+ break;
+ case EC_IOCTL_SLAVE:
+ ret = ec_ioctl_slave(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC:
+ ret = ec_ioctl_slave_sync(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC_PDO:
+ ret = ec_ioctl_slave_sync_pdo(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SYNC_PDO_ENTRY:
+ ret = ec_ioctl_slave_sync_pdo_entry(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN:
+ ret = ec_ioctl_domain(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN_FMMU:
+ ret = ec_ioctl_domain_fmmu(master, arg);
+ break;
+ case EC_IOCTL_DOMAIN_DATA:
+ ret = ec_ioctl_domain_data(master, arg);
+ break;
+ case EC_IOCTL_MASTER_DEBUG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_master_debug(master, arg);
+ break;
+ case EC_IOCTL_MASTER_RESCAN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_master_rescan(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_STATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_state(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO:
+ ret = ec_ioctl_slave_sdo(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_ENTRY:
+ ret = ec_ioctl_slave_sdo_entry(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_UPLOAD:
+ ret = ec_ioctl_slave_sdo_upload(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SDO_DOWNLOAD:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_sdo_download(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SII_READ:
+ ret = ec_ioctl_slave_sii_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SII_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_sii_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_REG_READ:
+ ret = ec_ioctl_slave_reg_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_REG_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_reg_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_FOE_READ:
+ ret = ec_ioctl_slave_foe_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_FOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_foe_write(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SOE_READ:
+ ret = ec_ioctl_slave_soe_read(master, arg);
+ break;
+ case EC_IOCTL_SLAVE_SOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_slave_soe_write(master, arg);
+ break;
+ case EC_IOCTL_CONFIG:
+ ret = ec_ioctl_config(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_PDO:
+ ret = ec_ioctl_config_pdo(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_PDO_ENTRY:
+ ret = ec_ioctl_config_pdo_entry(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_SDO:
+ ret = ec_ioctl_config_sdo(master, arg);
+ break;
+ case EC_IOCTL_CONFIG_IDN:
+ ret = ec_ioctl_config_idn(master, arg);
+ break;
+#ifdef EC_EOE
+ case EC_IOCTL_EOE_HANDLER:
+ ret = ec_ioctl_eoe_handler(master, arg);
+ break;
+#endif
+ case EC_IOCTL_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_CREATE_DOMAIN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_create_domain(master, arg, ctx);
+ break;
+ case EC_IOCTL_CREATE_SLAVE_CONFIG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_create_slave_config(master, arg, ctx);
+ break;
+ case EC_IOCTL_SELECT_REF_CLOCK:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_select_ref_clock(master, arg, ctx);
+ break;
+ case EC_IOCTL_ACTIVATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_activate(master, arg, ctx);
+ break;
+ case EC_IOCTL_DEACTIVATE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_deactivate(master, arg, ctx);
+ break;
+ case EC_IOCTL_SEND:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_send(master, arg, ctx);
+ break;
+ case EC_IOCTL_RECEIVE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_receive(master, arg, ctx);
+ break;
+ case EC_IOCTL_MASTER_STATE:
+ ret = ec_ioctl_master_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_MASTER_LINK_STATE:
+ ret = ec_ioctl_master_link_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_APP_TIME:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_app_time(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_REF:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_ref(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_REF_TO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_ref_to(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_SLAVES:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_slaves(master, arg, ctx);
+ break;
+ case EC_IOCTL_REF_CLOCK_TIME:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_ref_clock_time(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_MON_QUEUE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_mon_queue(master, arg, ctx);
+ break;
+ case EC_IOCTL_SYNC_MON_PROCESS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sync_mon_process(master, arg, ctx);
+ break;
+ case EC_IOCTL_RESET:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reset(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SYNC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_sync(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_WATCHDOG:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_watchdog(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_ADD_PDO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_add_pdo(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_CLEAR_PDOS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_clear_pdos(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_ADD_ENTRY:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_add_entry(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_CLEAR_ENTRIES:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_clear_entries(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_PDO_ENTRY:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_reg_pdo_entry(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_PDO_POS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_reg_pdo_pos(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_DC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_dc(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SDO:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_sdo(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_SIZE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_size(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_POP:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_pop(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_CLEAR:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_emerg_clear(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_EMERG_OVERRUNS:
+ ret = ec_ioctl_sc_emerg_overruns(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_SDO_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_sdo_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_REG_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_reg_request(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_VOE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_voe_handler(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_STATE:
+ ret = ec_ioctl_sc_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SC_IDN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_idn(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_SIZE:
+ ret = ec_ioctl_domain_size(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_OFFSET:
+ ret = ec_ioctl_domain_offset(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_PROCESS:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_domain_process(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_QUEUE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_domain_queue(master, arg, ctx);
+ break;
+ case EC_IOCTL_DOMAIN_STATE:
+ ret = ec_ioctl_domain_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_INDEX:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_index(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_TIMEOUT:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_timeout(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_STATE:
+ ret = ec_ioctl_sdo_request_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sdo_request_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_SDO_REQUEST_DATA:
+ ret = ec_ioctl_sdo_request_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_DATA:
+ ret = ec_ioctl_reg_request_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_STATE:
+ ret = ec_ioctl_reg_request_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reg_request_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_REG_REQUEST_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_reg_request_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_SEND_HEADER:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_send_header(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_REC_HEADER:
+ ret = ec_ioctl_voe_rec_header(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_READ_NOSYNC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_read_nosync(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_EXEC:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_voe_exec(master, arg, ctx);
+ break;
+ case EC_IOCTL_VOE_DATA:
+ ret = ec_ioctl_voe_data(master, arg, ctx);
+ break;
+ case EC_IOCTL_SET_SEND_INTERVAL:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_set_send_interval(master, arg, ctx);
+ break;
+ default:
+ ret = -ENOTTY;
+ break;
+ }
+
+#if DEBUG_LATENCY
+ b = get_cycles();
+ t = (unsigned int) ((b - a) * 1000LL) / cpu_khz;
+ if (t > 50) {
+ EC_MASTER_WARN(master, "ioctl(0x%02x) took %u us.\n",
+ _IOC_NR(cmd), t);
+ }
+#endif
+
+ return ret;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/rtdm.c b/net/ethercat/master/rtdm.c
new file mode 100644
index 000000000000..5e6e7bb70206
--- /dev/null
+++ b/net/ethercat/master/rtdm.c
@@ -0,0 +1,241 @@
+/*****************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2009-2010 Moehwald GmbH B. Benner
+ * 2011 IgH Andreas Stewering-Bone
+ * 2012 Florian Pose <fp@igh-essen.com>
+ *
+ * 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 as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * 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
+ * RTDM interface.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/mman.h>
+
+#include <rtdm/rtdm_driver.h>
+
+#include "master.h"
+#include "ioctl.h"
+#include "rtdm.h"
+
+/** Set to 1 to enable device operations debugging.
+ */
+#define DEBUG 0
+
+/****************************************************************************/
+
+/** Context structure for an open RTDM file handle.
+ */
+typedef struct {
+ rtdm_user_info_t *user_info; /**< RTDM user data. */
+ ec_ioctl_context_t ioctl_ctx; /**< Context structure. */
+} ec_rtdm_context_t;
+
+/****************************************************************************/
+
+int ec_rtdm_open(struct rtdm_dev_context *, rtdm_user_info_t *, int);
+int ec_rtdm_close(struct rtdm_dev_context *, rtdm_user_info_t *);
+int ec_rtdm_ioctl(struct rtdm_dev_context *, rtdm_user_info_t *,
+ unsigned int, void __user *);
+
+/****************************************************************************/
+
+/** Initialize an RTDM device.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_rtdm_dev_init(
+ ec_rtdm_dev_t *rtdm_dev, /**< EtherCAT RTDM device. */
+ ec_master_t *master /**< EtherCAT master. */
+ )
+{
+ int ret;
+
+ rtdm_dev->master = master;
+
+ rtdm_dev->dev = kzalloc(sizeof(struct rtdm_device), GFP_KERNEL);
+ if (!rtdm_dev->dev) {
+ EC_MASTER_ERR(master, "Failed to reserve memory for RTDM device.\n");
+ return -ENOMEM;
+ }
+
+ rtdm_dev->dev->struct_version = RTDM_DEVICE_STRUCT_VER;
+ rtdm_dev->dev->device_flags = RTDM_NAMED_DEVICE;
+ rtdm_dev->dev->context_size = sizeof(ec_rtdm_context_t);
+ snprintf(rtdm_dev->dev->device_name, RTDM_MAX_DEVNAME_LEN,
+ "EtherCAT%u", master->index);
+ rtdm_dev->dev->open_nrt = ec_rtdm_open;
+ rtdm_dev->dev->ops.close_nrt = ec_rtdm_close;
+ rtdm_dev->dev->ops.ioctl_rt = ec_rtdm_ioctl;
+ rtdm_dev->dev->ops.ioctl_nrt = ec_rtdm_ioctl;
+ rtdm_dev->dev->device_class = RTDM_CLASS_EXPERIMENTAL;
+ rtdm_dev->dev->device_sub_class = 222;
+ rtdm_dev->dev->driver_name = "EtherCAT";
+ rtdm_dev->dev->driver_version = RTDM_DRIVER_VER(1, 0, 2);
+ rtdm_dev->dev->peripheral_name = rtdm_dev->dev->device_name;
+ rtdm_dev->dev->provider_name = "EtherLab Community";
+ rtdm_dev->dev->proc_name = rtdm_dev->dev->device_name;
+ rtdm_dev->dev->device_data = rtdm_dev; /* pointer to parent */
+
+ EC_MASTER_INFO(master, "Registering RTDM device %s.\n",
+ rtdm_dev->dev->driver_name);
+ ret = rtdm_dev_register(rtdm_dev->dev);
+ if (ret) {
+ EC_MASTER_ERR(master, "Initialization of RTDM interface failed"
+ " (return value %i).\n", ret);
+ kfree(rtdm_dev->dev);
+ }
+
+ return ret;
+}
+
+/****************************************************************************/
+
+/** Clear an RTDM device.
+ */
+void ec_rtdm_dev_clear(
+ ec_rtdm_dev_t *rtdm_dev /**< EtherCAT RTDM device. */
+ )
+{
+ int ret;
+
+ EC_MASTER_INFO(rtdm_dev->master, "Unregistering RTDM device %s.\n",
+ rtdm_dev->dev->driver_name);
+ ret = rtdm_dev_unregister(rtdm_dev->dev, 1000 /* poll delay [ms] */);
+ if (ret < 0) {
+ EC_MASTER_WARN(rtdm_dev->master,
+ "Failed to unregister RTDM device (code %i).\n", ret);
+ }
+
+ kfree(rtdm_dev->dev);
+}
+
+/****************************************************************************/
+
+/** Driver open.
+ *
+ * \return Always zero (success).
+ */
+int ec_rtdm_open(
+ struct rtdm_dev_context *context, /**< Context. */
+ rtdm_user_info_t *user_info, /**< User data. */
+ int oflags /**< Open flags. */
+ )
+{
+ ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private;
+#if DEBUG
+ ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data;
+#endif
+
+ ctx->user_info = user_info;
+ ctx->ioctl_ctx.writable = oflags & O_WRONLY || oflags & O_RDWR;
+ ctx->ioctl_ctx.requested = 0;
+ ctx->ioctl_ctx.process_data = NULL;
+ ctx->ioctl_ctx.process_data_size = 0;
+
+#if DEBUG
+ EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s opened.\n",
+ context->device->device_name);
+#endif
+ return 0;
+}
+
+/****************************************************************************/
+
+/** Driver close.
+ *
+ * \return Always zero (success).
+ */
+int ec_rtdm_close(
+ struct rtdm_dev_context *context, /**< Context. */
+ rtdm_user_info_t *user_info /**< User data. */
+ )
+{
+ ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private;
+ ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data;
+
+ if (ctx->ioctl_ctx.requested) {
+ ecrt_release_master(rtdm_dev->master);
+ }
+
+#if DEBUG
+ EC_MASTER_INFO(rtdm_dev->master, "RTDM device %s closed.\n",
+ context->device->device_name);
+#endif
+ return 0;
+}
+
+/****************************************************************************/
+
+/** Driver ioctl.
+ *
+ * \return ioctl() return code.
+ */
+int ec_rtdm_ioctl(
+ struct rtdm_dev_context *context, /**< Context. */
+ rtdm_user_info_t *user_info, /**< User data. */
+ unsigned int request, /**< Request. */
+ void __user *arg /**< Argument. */
+ )
+{
+ ec_rtdm_context_t *ctx = (ec_rtdm_context_t *) context->dev_private;
+ ec_rtdm_dev_t *rtdm_dev = (ec_rtdm_dev_t *) context->device->device_data;
+
+#if DEBUG
+ EC_MASTER_INFO(rtdm_dev->master, "ioctl(request = %u, ctl = %02x)"
+ " on RTDM device %s.\n", request, _IOC_NR(request),
+ context->device->device_name);
+#endif
+ return ec_ioctl_rtdm(rtdm_dev->master, &ctx->ioctl_ctx, request, arg);
+}
+
+/****************************************************************************/
+
+/** Memory-map process data to user space.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_rtdm_mmap(
+ ec_ioctl_context_t *ioctl_ctx, /**< Context. */
+ void **user_address /**< Userspace address. */
+ )
+{
+ ec_rtdm_context_t *ctx =
+ container_of(ioctl_ctx, ec_rtdm_context_t, ioctl_ctx);
+ int ret;
+
+ ret = rtdm_mmap_to_user(ctx->user_info,
+ ioctl_ctx->process_data, ioctl_ctx->process_data_size,
+ PROT_READ | PROT_WRITE,
+ user_address,
+ NULL, NULL);
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+/****************************************************************************/
diff --git a/net/ethercat/master/rtdm.h b/net/ethercat/master/rtdm.h
new file mode 100644
index 000000000000..3d1b49dfd248
--- /dev/null
+++ b/net/ethercat/master/rtdm.h
@@ -0,0 +1,56 @@
+/*****************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2012 Florian Pose <fp@igh-essen.com>
+ *
+ * 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 as published
+ * by the Free Software Foundation; version 2 of the License.
+ *
+ * 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, see <http://www.gnu.org/licenses/>.
+ *
+ * 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
+ * RTDM interface.
+ */
+
+#ifndef __EC_RTDM_H__
+#define __EC_RTDM_H__
+
+#include "../include/ecrt.h" /* ec_master_t */
+
+/*****************************************************************************/
+
+struct rtdm_device;
+
+/*****************************************************************************/
+
+/** EtherCAT RTDM device.
+ */
+typedef struct ec_rtdm_dev {
+ ec_master_t *master; /**< Master pointer. */
+ struct rtdm_device *dev; /**< RTDM device. */
+} ec_rtdm_dev_t;
+
+/*****************************************************************************/
+
+int ec_rtdm_dev_init(ec_rtdm_dev_t *, ec_master_t *);
+void ec_rtdm_dev_clear(ec_rtdm_dev_t *);
+
+/****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/sdo.c b/net/ethercat/master/sdo.c
new file mode 100644
index 000000000000..6d398e6f05c3
--- /dev/null
+++ b/net/ethercat/master/sdo.c
@@ -0,0 +1,132 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ CANopen SDO functions.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "master.h"
+
+#include "sdo.h"
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_sdo_init(
+ ec_sdo_t *sdo, /**< SDO. */
+ ec_slave_t *slave, /**< Parent slave. */
+ uint16_t index /**< SDO index. */
+ )
+{
+ sdo->slave = slave;
+ sdo->index = index;
+ sdo->object_code = 0x00;
+ sdo->name = NULL;
+ sdo->max_subindex = 0;
+ INIT_LIST_HEAD(&sdo->entries);
+}
+
+/*****************************************************************************/
+
+/** SDO destructor.
+ *
+ * Clears and frees an SDO object.
+ */
+void ec_sdo_clear(
+ ec_sdo_t *sdo /**< SDO. */
+ )
+{
+ ec_sdo_entry_t *entry, *next;
+
+ // free all entries
+ list_for_each_entry_safe(entry, next, &sdo->entries, list) {
+ list_del(&entry->list);
+ ec_sdo_entry_clear(entry);
+ kfree(entry);
+ }
+
+ if (sdo->name)
+ kfree(sdo->name);
+}
+
+/*****************************************************************************/
+
+/** Get an SDO entry from an SDO via its subindex.
+ *
+ * \retval >0 Pointer to the requested SDO entry.
+ * \retval NULL SDO entry not found.
+ */
+ec_sdo_entry_t *ec_sdo_get_entry(
+ ec_sdo_t *sdo, /**< SDO. */
+ uint8_t subindex /**< Entry subindex. */
+ )
+{
+ ec_sdo_entry_t *entry;
+
+ list_for_each_entry(entry, &sdo->entries, list) {
+ if (entry->subindex != subindex)
+ continue;
+ return entry;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Get an SDO entry from an SDO via its subindex.
+ *
+ * const version.
+ *
+ * \retval >0 Pointer to the requested SDO entry.
+ * \retval NULL SDO entry not found.
+ */
+const ec_sdo_entry_t *ec_sdo_get_entry_const(
+ const ec_sdo_t *sdo, /**< SDO. */
+ uint8_t subindex /**< Entry subindex. */
+ )
+{
+ const ec_sdo_entry_t *entry;
+
+ list_for_each_entry(entry, &sdo->entries, list) {
+ if (entry->subindex != subindex)
+ continue;
+ return entry;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/sdo.h b/net/ethercat/master/sdo.h
new file mode 100644
index 000000000000..0af7dae03e24
--- /dev/null
+++ b/net/ethercat/master/sdo.h
@@ -0,0 +1,69 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CANopen SDO structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SDO_H__
+#define __EC_SDO_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "sdo_entry.h"
+
+/*****************************************************************************/
+
+/** CANopen SDO.
+ */
+struct ec_sdo {
+ struct list_head list; /**< List item. */
+ ec_slave_t *slave; /**< Parent slave. */
+ uint16_t index; /**< SDO index. */
+ uint8_t object_code; /**< Object code. */
+ char *name; /**< SDO name. */
+ uint8_t max_subindex; /**< Maximum subindex. */
+ struct list_head entries; /**< List of entries. */
+};
+
+/*****************************************************************************/
+
+void ec_sdo_init(ec_sdo_t *, ec_slave_t *, uint16_t);
+void ec_sdo_clear(ec_sdo_t *);
+
+ec_sdo_entry_t *ec_sdo_get_entry(ec_sdo_t *, uint8_t);
+const ec_sdo_entry_t *ec_sdo_get_entry_const(const ec_sdo_t *, uint8_t);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/sdo_entry.c b/net/ethercat/master/sdo_entry.c
new file mode 100644
index 000000000000..acb3364ed2ee
--- /dev/null
+++ b/net/ethercat/master/sdo_entry.c
@@ -0,0 +1,77 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ CANopen over EtherCAT SDO entry functions.
+*/
+
+/*****************************************************************************/
+
+#include <linux/slab.h>
+
+#include "sdo_entry.h"
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_sdo_entry_init(
+ ec_sdo_entry_t *entry, /**< SDO entry. */
+ ec_sdo_t *sdo, /**< Parent SDO. */
+ uint8_t subindex /**< Subindex. */
+ )
+{
+ entry->sdo = sdo;
+ entry->subindex = subindex;
+ entry->data_type = 0x0000;
+ entry->bit_length = 0;
+ entry->read_access[EC_SDO_ENTRY_ACCESS_PREOP] = 0;
+ entry->read_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = 0;
+ entry->read_access[EC_SDO_ENTRY_ACCESS_OP] = 0;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_PREOP] = 0;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_SAFEOP] = 0;
+ entry->write_access[EC_SDO_ENTRY_ACCESS_OP] = 0;
+ entry->description = NULL;
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_sdo_entry_clear(
+ ec_sdo_entry_t *entry /**< SDO entry. */
+ )
+{
+
+ if (entry->description)
+ kfree(entry->description);
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/sdo_entry.h b/net/ethercat/master/sdo_entry.h
new file mode 100644
index 000000000000..b461f3e63b34
--- /dev/null
+++ b/net/ethercat/master/sdo_entry.h
@@ -0,0 +1,72 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CANopen SDO entry structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SDO_ENTRY_H__
+#define __EC_SDO_ENTRY_H__
+
+#include <linux/list.h>
+#include <linux/kobject.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+struct ec_sdo;
+typedef struct ec_sdo ec_sdo_t; /**< \see ec_sdo. */
+
+/*****************************************************************************/
+
+/** CANopen SDO entry.
+ */
+typedef struct {
+ struct list_head list; /**< List item. */
+ ec_sdo_t *sdo; /**< Parent SDO. */
+ uint8_t subindex; /**< Subindex. */
+ uint16_t data_type; /**< Data type. */
+ uint16_t bit_length; /**< Data size in bit. */
+ uint8_t read_access[EC_SDO_ENTRY_ACCESS_COUNT]; /**< Read access. */
+ uint8_t write_access[EC_SDO_ENTRY_ACCESS_COUNT]; /**< Write access. */
+ char *description; /**< Description. */
+} ec_sdo_entry_t;
+
+/*****************************************************************************/
+
+void ec_sdo_entry_init(ec_sdo_entry_t *, ec_sdo_t *, uint8_t);
+void ec_sdo_entry_clear(ec_sdo_entry_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/sdo_request.c b/net/ethercat/master/sdo_request.c
new file mode 100644
index 000000000000..a1ba8997b825
--- /dev/null
+++ b/net/ethercat/master/sdo_request.c
@@ -0,0 +1,258 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ * Canopen over EtherCAT SDO request functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "sdo_request.h"
+
+/*****************************************************************************/
+
+/** Default timeout in ms to wait for SDO transfer responses.
+ */
+#define EC_SDO_REQUEST_RESPONSE_TIMEOUT 1000
+
+/*****************************************************************************/
+
+void ec_sdo_request_clear_data(ec_sdo_request_t *);
+
+/*****************************************************************************/
+
+/** SDO request constructor.
+ */
+void ec_sdo_request_init(
+ ec_sdo_request_t *req /**< SDO request. */
+ )
+{
+ req->complete_access = 0;
+ req->data = NULL;
+ req->mem_size = 0;
+ req->data_size = 0;
+ req->dir = EC_DIR_INVALID;
+ req->issue_timeout = 0; // no timeout
+ req->response_timeout = EC_SDO_REQUEST_RESPONSE_TIMEOUT;
+ req->state = EC_INT_REQUEST_INIT;
+ req->errno = 0;
+ req->abort_code = 0x00000000;
+}
+
+/*****************************************************************************/
+
+/** SDO request destructor.
+ */
+void ec_sdo_request_clear(
+ ec_sdo_request_t *req /**< SDO request. */
+ )
+{
+ ec_sdo_request_clear_data(req);
+}
+
+/*****************************************************************************/
+
+/** Copy another SDO request.
+ *
+ * \attention Only the index subindex and data are copied.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_sdo_request_copy(
+ ec_sdo_request_t *req, /**< SDO request. */
+ const ec_sdo_request_t *other /**< Other SDO request to copy from. */
+ )
+{
+ req->complete_access = other->complete_access;
+ req->index = other->index;
+ req->subindex = other->subindex;
+ return ec_sdo_request_copy_data(req, other->data, other->data_size);
+}
+
+/*****************************************************************************/
+
+/** SDO request destructor.
+ */
+void ec_sdo_request_clear_data(
+ ec_sdo_request_t *req /**< SDO request. */
+ )
+{
+ if (req->data) {
+ kfree(req->data);
+ req->data = NULL;
+ }
+
+ req->mem_size = 0;
+ req->data_size = 0;
+}
+
+/*****************************************************************************/
+
+/** Pre-allocates the data memory.
+ *
+ * If the \a mem_size is already bigger than \a size, nothing is done.
+ *
+ * \return 0 on success, otherwise -ENOMEM.
+ */
+int ec_sdo_request_alloc(
+ ec_sdo_request_t *req, /**< SDO request. */
+ size_t size /**< Data size to allocate. */
+ )
+{
+ if (size <= req->mem_size)
+ return 0;
+
+ ec_sdo_request_clear_data(req);
+
+ if (!(req->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate %zu bytes of SDO memory.\n", size);
+ return -ENOMEM;
+ }
+
+ req->mem_size = size;
+ req->data_size = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Copies SDO data from an external source.
+ *
+ * If the \a mem_size is to small, new memory is allocated.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_sdo_request_copy_data(
+ ec_sdo_request_t *req, /**< SDO request. */
+ const uint8_t *source, /**< Source data. */
+ size_t size /**< Number of bytes in \a source. */
+ )
+{
+ int ret = ec_sdo_request_alloc(req, size);
+ if (ret < 0)
+ return ret;
+
+ memcpy(req->data, source, size);
+ req->data_size = size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Checks, if the timeout was exceeded.
+ *
+ * \return non-zero if the timeout was exceeded, else zero.
+ */
+int ec_sdo_request_timed_out(const ec_sdo_request_t *req /**< SDO request. */)
+{
+ return req->issue_timeout
+ && jiffies - req->jiffies_start > HZ * req->issue_timeout / 1000;
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+void ecrt_sdo_request_index(ec_sdo_request_t *req, uint16_t index,
+ uint8_t subindex)
+{
+ req->index = index;
+ req->subindex = subindex;
+}
+
+/*****************************************************************************/
+
+void ecrt_sdo_request_timeout(ec_sdo_request_t *req, uint32_t timeout)
+{
+ req->issue_timeout = timeout;
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_sdo_request_data(ec_sdo_request_t *req)
+{
+ return req->data;
+}
+
+/*****************************************************************************/
+
+size_t ecrt_sdo_request_data_size(const ec_sdo_request_t *req)
+{
+ return req->data_size;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_sdo_request_state(const ec_sdo_request_t *req)
+{
+ return ec_request_state_translation_table[req->state];
+}
+
+/*****************************************************************************/
+
+void ecrt_sdo_request_read(ec_sdo_request_t *req)
+{
+ req->dir = EC_DIR_INPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->errno = 0;
+ req->abort_code = 0x00000000;
+ req->jiffies_start = jiffies;
+}
+
+/*****************************************************************************/
+
+void ecrt_sdo_request_write(ec_sdo_request_t *req)
+{
+ req->dir = EC_DIR_OUTPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->errno = 0;
+ req->abort_code = 0x00000000;
+ req->jiffies_start = jiffies;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_sdo_request_index);
+EXPORT_SYMBOL(ecrt_sdo_request_timeout);
+EXPORT_SYMBOL(ecrt_sdo_request_data);
+EXPORT_SYMBOL(ecrt_sdo_request_data_size);
+EXPORT_SYMBOL(ecrt_sdo_request_state);
+EXPORT_SYMBOL(ecrt_sdo_request_read);
+EXPORT_SYMBOL(ecrt_sdo_request_write);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/sdo_request.h b/net/ethercat/master/sdo_request.h
new file mode 100644
index 000000000000..80b1c101c2d3
--- /dev/null
+++ b/net/ethercat/master/sdo_request.h
@@ -0,0 +1,83 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 CANopen SDO request structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SDO_REQUEST_H__
+#define __EC_SDO_REQUEST_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** CANopen SDO request.
+ */
+struct ec_sdo_request {
+ struct list_head list; /**< List item. */
+ uint16_t index; /**< SDO index. */
+ uint8_t subindex; /**< SDO subindex. */
+ uint8_t *data; /**< Pointer to SDO data. */
+ size_t mem_size; /**< Size of SDO data memory. */
+ size_t data_size; /**< Size of SDO data. */
+ uint8_t complete_access; /**< SDO shall be transferred completely. */
+ uint32_t issue_timeout; /**< Maximum time in ms, the processing of the
+ request may take. */
+ uint32_t response_timeout; /**< Maximum time in ms, the transfer is
+ retried, if the slave does not respond. */
+ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means downloading to
+ the slave, EC_DIR_INPUT means uploading from the
+ slave. */
+ ec_internal_request_state_t state; /**< SDO request state. */
+ unsigned long jiffies_start; /**< Jiffies, when the request was issued. */
+ unsigned long jiffies_sent; /**< Jiffies, when the upload/download
+ request was sent. */
+ int errno; /**< Error number. */
+ uint32_t abort_code; /**< SDO request abort code. Zero on success. */
+};
+
+/*****************************************************************************/
+
+void ec_sdo_request_init(ec_sdo_request_t *);
+void ec_sdo_request_clear(ec_sdo_request_t *);
+
+int ec_sdo_request_copy(ec_sdo_request_t *, const ec_sdo_request_t *);
+int ec_sdo_request_alloc(ec_sdo_request_t *, size_t);
+int ec_sdo_request_copy_data(ec_sdo_request_t *, const uint8_t *, size_t);
+int ec_sdo_request_timed_out(const ec_sdo_request_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/slave.c b/net/ethercat/master/slave.c
new file mode 100644
index 000000000000..c081fe3f1c93
--- /dev/null
+++ b/net/ethercat/master/slave.c
@@ -0,0 +1,1000 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 slave methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/delay.h>
+
+#include "globals.h"
+#include "datagram.h"
+#include "master.h"
+#include "slave_config.h"
+
+#include "slave.h"
+
+/*****************************************************************************/
+
+extern const ec_code_msg_t al_status_messages[];
+
+/*****************************************************************************/
+
+char *ec_slave_sii_string(ec_slave_t *, unsigned int);
+
+/*****************************************************************************/
+
+/**
+ Slave constructor.
+ \return 0 in case of success, else < 0
+*/
+
+void ec_slave_init(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ ec_master_t *master, /**< EtherCAT master */
+ ec_device_index_t dev_idx, /**< Device index. */
+ uint16_t ring_position, /**< ring position */
+ uint16_t station_address /**< station address to configure */
+ )
+{
+ unsigned int i;
+
+ slave->master = master;
+ slave->device_index = dev_idx;
+ slave->ring_position = ring_position;
+ slave->station_address = station_address;
+ slave->effective_alias = 0x0000;
+
+ slave->config = NULL;
+ slave->requested_state = EC_SLAVE_STATE_PREOP;
+ slave->current_state = EC_SLAVE_STATE_UNKNOWN;
+ slave->error_flag = 0;
+ slave->force_config = 0;
+ slave->configured_rx_mailbox_offset = 0x0000;
+ slave->configured_rx_mailbox_size = 0x0000;
+ slave->configured_tx_mailbox_offset = 0x0000;
+ slave->configured_tx_mailbox_size = 0x0000;
+
+ slave->base_type = 0;
+ slave->base_revision = 0;
+ slave->base_build = 0;
+ slave->base_fmmu_count = 0;
+ slave->base_sync_count = 0;
+
+ for (i = 0; i < EC_MAX_PORTS; i++) {
+ slave->ports[i].desc = EC_PORT_NOT_IMPLEMENTED;
+
+ slave->ports[i].link.link_up = 0;
+ slave->ports[i].link.loop_closed = 0;
+ slave->ports[i].link.signal_detected = 0;
+ slave->sii.physical_layer[i] = 0xFF;
+
+ slave->ports[i].receive_time = 0U;
+
+ slave->ports[i].next_slave = NULL;
+ slave->ports[i].delay_to_next_dc = 0U;
+ }
+
+ slave->base_fmmu_bit_operation = 0;
+ slave->base_dc_supported = 0;
+ slave->base_dc_range = EC_DC_32;
+ slave->has_dc_system_time = 0;
+ slave->transmission_delay = 0U;
+
+ slave->sii_words = NULL;
+ slave->sii_nwords = 0;
+
+ slave->sii.alias = 0x0000;
+ slave->sii.vendor_id = 0x00000000;
+ slave->sii.product_code = 0x00000000;
+ slave->sii.revision_number = 0x00000000;
+ slave->sii.serial_number = 0x00000000;
+ slave->sii.boot_rx_mailbox_offset = 0x0000;
+ slave->sii.boot_rx_mailbox_size = 0x0000;
+ slave->sii.boot_tx_mailbox_offset = 0x0000;
+ slave->sii.boot_tx_mailbox_size = 0x0000;
+ slave->sii.std_rx_mailbox_offset = 0x0000;
+ slave->sii.std_rx_mailbox_size = 0x0000;
+ slave->sii.std_tx_mailbox_offset = 0x0000;
+ slave->sii.std_tx_mailbox_size = 0x0000;
+ slave->sii.mailbox_protocols = 0;
+
+ slave->sii.strings = NULL;
+ slave->sii.string_count = 0;
+
+ slave->sii.has_general = 0;
+ slave->sii.group = NULL;
+ slave->sii.image = NULL;
+ slave->sii.order = NULL;
+ slave->sii.name = NULL;
+ memset(&slave->sii.coe_details, 0x00, sizeof(ec_sii_coe_details_t));
+ memset(&slave->sii.general_flags, 0x00, sizeof(ec_sii_general_flags_t));
+ slave->sii.current_on_ebus = 0;
+
+ slave->sii.syncs = NULL;
+ slave->sii.sync_count = 0;
+
+ INIT_LIST_HEAD(&slave->sii.pdos);
+
+ INIT_LIST_HEAD(&slave->sdo_dictionary);
+
+ slave->sdo_dictionary_fetched = 0;
+ slave->jiffies_preop = 0;
+
+ INIT_LIST_HEAD(&slave->sdo_requests);
+ INIT_LIST_HEAD(&slave->reg_requests);
+ INIT_LIST_HEAD(&slave->foe_requests);
+ INIT_LIST_HEAD(&slave->soe_requests);
+
+ // create state machine object
+ ec_fsm_slave_init(&slave->fsm, slave);
+}
+
+/*****************************************************************************/
+
+/**
+ Slave destructor.
+ Clears and frees a slave object.
+*/
+
+void ec_slave_clear(ec_slave_t *slave /**< EtherCAT slave */)
+{
+ ec_sdo_t *sdo, *next_sdo;
+ unsigned int i;
+ ec_pdo_t *pdo, *next_pdo;
+
+ // abort all pending requests
+
+ while (!list_empty(&slave->sdo_requests)) {
+ ec_sdo_request_t *request =
+ list_entry(slave->sdo_requests.next, ec_sdo_request_t, list);
+ list_del_init(&request->list); // dequeue
+ EC_SLAVE_WARN(slave, "Discarding SDO request,"
+ " slave about to be deleted.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ }
+
+ while (!list_empty(&slave->reg_requests)) {
+ ec_reg_request_t *reg =
+ list_entry(slave->reg_requests.next, ec_reg_request_t, list);
+ list_del_init(&reg->list); // dequeue
+ EC_SLAVE_WARN(slave, "Discarding register request,"
+ " slave about to be deleted.\n");
+ reg->state = EC_INT_REQUEST_FAILURE;
+ }
+
+ while (!list_empty(&slave->foe_requests)) {
+ ec_foe_request_t *request =
+ list_entry(slave->foe_requests.next, ec_foe_request_t, list);
+ list_del_init(&request->list); // dequeue
+ EC_SLAVE_WARN(slave, "Discarding FoE request,"
+ " slave about to be deleted.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ }
+
+ while (!list_empty(&slave->soe_requests)) {
+ ec_soe_request_t *request =
+ list_entry(slave->soe_requests.next, ec_soe_request_t, list);
+ list_del_init(&request->list); // dequeue
+ EC_SLAVE_WARN(slave, "Discarding SoE request,"
+ " slave about to be deleted.\n");
+ request->state = EC_INT_REQUEST_FAILURE;
+ }
+
+ wake_up_all(&slave->master->request_queue);
+
+ if (slave->config) {
+ ec_slave_config_detach(slave->config);
+ }
+
+ // free all SDOs
+ list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
+ list_del(&sdo->list);
+ ec_sdo_clear(sdo);
+ kfree(sdo);
+ }
+
+ // free all strings
+ if (slave->sii.strings) {
+ for (i = 0; i < slave->sii.string_count; i++)
+ kfree(slave->sii.strings[i]);
+ kfree(slave->sii.strings);
+ }
+
+ // free all sync managers
+ ec_slave_clear_sync_managers(slave);
+
+ // free all SII PDOs
+ list_for_each_entry_safe(pdo, next_pdo, &slave->sii.pdos, list) {
+ list_del(&pdo->list);
+ ec_pdo_clear(pdo);
+ kfree(pdo);
+ }
+
+ if (slave->sii_words) {
+ kfree(slave->sii_words);
+ }
+
+ ec_fsm_slave_clear(&slave->fsm);
+}
+
+/*****************************************************************************/
+
+/** Clear the sync manager array.
+ */
+void ec_slave_clear_sync_managers(ec_slave_t *slave /**< EtherCAT slave. */)
+{
+ unsigned int i;
+
+ if (slave->sii.syncs) {
+ for (i = 0; i < slave->sii.sync_count; i++) {
+ ec_sync_clear(&slave->sii.syncs[i]);
+ }
+ kfree(slave->sii.syncs);
+ slave->sii.syncs = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ * Sets the application state of a slave.
+ */
+
+void ec_slave_set_state(ec_slave_t *slave, /**< EtherCAT slave */
+ ec_slave_state_t new_state /**< new application state */
+ )
+{
+ if (new_state != slave->current_state) {
+ if (slave->master->debug_level) {
+ char old_state[EC_STATE_STRING_SIZE],
+ cur_state[EC_STATE_STRING_SIZE];
+ ec_state_string(slave->current_state, old_state, 0);
+ ec_state_string(new_state, cur_state, 0);
+ EC_SLAVE_DBG(slave, 0, "%s -> %s.\n", old_state, cur_state);
+ }
+ slave->current_state = new_state;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ * Request a slave state and resets the error flag.
+ */
+
+void ec_slave_request_state(ec_slave_t *slave, /**< EtherCAT slave */
+ ec_slave_state_t state /**< new state */
+ )
+{
+ slave->requested_state = state;
+ slave->error_flag = 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Fetches data from a STRING category.
+ \todo range checking
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_fetch_sii_strings(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ const uint8_t *data, /**< category data */
+ size_t data_size /**< number of bytes */
+ )
+{
+ int i, err;
+ size_t size;
+ off_t offset;
+
+ slave->sii.string_count = data[0];
+
+ if (slave->sii.string_count) {
+ if (!(slave->sii.strings =
+ kmalloc(sizeof(char *) * slave->sii.string_count,
+ GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate string array memory.\n");
+ err = -ENOMEM;
+ goto out_zero;
+ }
+
+ offset = 1;
+ for (i = 0; i < slave->sii.string_count; i++) {
+ size = data[offset];
+ // allocate memory for string structure and data at a single blow
+ if (!(slave->sii.strings[i] =
+ kmalloc(sizeof(char) * size + 1, GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate string memory.\n");
+ err = -ENOMEM;
+ goto out_free;
+ }
+ memcpy(slave->sii.strings[i], data + offset + 1, size);
+ slave->sii.strings[i][size] = 0x00; // append binary zero
+ offset += 1 + size;
+ }
+ }
+
+ return 0;
+
+out_free:
+ for (i--; i >= 0; i--)
+ kfree(slave->sii.strings[i]);
+ kfree(slave->sii.strings);
+ slave->sii.strings = NULL;
+out_zero:
+ slave->sii.string_count = 0;
+ return err;
+}
+
+/*****************************************************************************/
+
+/**
+ Fetches data from a GENERAL category.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_fetch_sii_general(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ const uint8_t *data, /**< category data */
+ size_t data_size /**< size in bytes */
+ )
+{
+ unsigned int i;
+ uint8_t flags;
+
+ if (data_size != 32) {
+ EC_SLAVE_ERR(slave, "Wrong size of general category (%zu/32).\n",
+ data_size);
+ return -EINVAL;
+ }
+
+ slave->sii.group = ec_slave_sii_string(slave, data[0]);
+ slave->sii.image = ec_slave_sii_string(slave, data[1]);
+ slave->sii.order = ec_slave_sii_string(slave, data[2]);
+ slave->sii.name = ec_slave_sii_string(slave, data[3]);
+
+ for (i = 0; i < 4; i++)
+ slave->sii.physical_layer[i] =
+ (data[4] & (0x03 << (i * 2))) >> (i * 2);
+
+ // read CoE details
+ flags = EC_READ_U8(data + 5);
+ slave->sii.coe_details.enable_sdo = (flags >> 0) & 0x01;
+ slave->sii.coe_details.enable_sdo_info = (flags >> 1) & 0x01;
+ slave->sii.coe_details.enable_pdo_assign = (flags >> 2) & 0x01;
+ slave->sii.coe_details.enable_pdo_configuration = (flags >> 3) & 0x01;
+ slave->sii.coe_details.enable_upload_at_startup = (flags >> 4) & 0x01;
+ slave->sii.coe_details.enable_sdo_complete_access = (flags >> 5) & 0x01;
+
+ // read general flags
+ flags = EC_READ_U8(data + 0x000B);
+ slave->sii.general_flags.enable_safeop = (flags >> 0) & 0x01;
+ slave->sii.general_flags.enable_not_lrw = (flags >> 1) & 0x01;
+
+ slave->sii.current_on_ebus = EC_READ_S16(data + 0x0C);
+ slave->sii.has_general = 1;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Fetches data from a SYNC MANAGER category.
+ *
+ * Appends the sync managers described in the category to the existing ones.
+ *
+ * \return 0 in case of success, else < 0
+ */
+int ec_slave_fetch_sii_syncs(
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ const uint8_t *data, /**< Category data. */
+ size_t data_size /**< Number of bytes. */
+ )
+{
+ unsigned int i, count, total_count;
+ ec_sync_t *sync;
+ size_t memsize;
+ ec_sync_t *syncs;
+ uint8_t index;
+
+ // one sync manager struct is 4 words long
+ if (data_size % 8) {
+ EC_SLAVE_ERR(slave, "Invalid SII sync manager category size %zu.\n",
+ data_size);
+ return -EINVAL;
+ }
+
+ count = data_size / 8;
+
+ if (count) {
+ total_count = count + slave->sii.sync_count;
+ if (total_count > EC_MAX_SYNC_MANAGERS) {
+ EC_SLAVE_ERR(slave, "Exceeded maximum number of"
+ " sync managers!\n");
+ return -EOVERFLOW;
+ }
+ memsize = sizeof(ec_sync_t) * total_count;
+ if (!(syncs = kmalloc(memsize, GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate %zu bytes"
+ " for sync managers.\n", memsize);
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < slave->sii.sync_count; i++)
+ ec_sync_init_copy(syncs + i, slave->sii.syncs + i);
+
+ // initialize new sync managers
+ for (i = 0; i < count; i++, data += 8) {
+ index = i + slave->sii.sync_count;
+ sync = &syncs[index];
+
+ ec_sync_init(sync, slave);
+ sync->physical_start_address = EC_READ_U16(data);
+ sync->default_length = EC_READ_U16(data + 2);
+ sync->control_register = EC_READ_U8(data + 4);
+ sync->enable = EC_READ_U8(data + 6);
+ }
+
+ if (slave->sii.syncs)
+ kfree(slave->sii.syncs);
+ slave->sii.syncs = syncs;
+ slave->sii.sync_count = total_count;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Fetches data from a [RT]xPDO category.
+ \return 0 in case of success, else < 0
+*/
+
+int ec_slave_fetch_sii_pdos(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ const uint8_t *data, /**< category data */
+ size_t data_size, /**< number of bytes */
+ ec_direction_t dir /**< PDO direction. */
+ )
+{
+ int ret;
+ ec_pdo_t *pdo;
+ ec_pdo_entry_t *entry;
+ unsigned int entry_count, i;
+
+ while (data_size >= 8) {
+ if (!(pdo = kmalloc(sizeof(ec_pdo_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate PDO memory.\n");
+ return -ENOMEM;
+ }
+
+ ec_pdo_init(pdo);
+ pdo->index = EC_READ_U16(data);
+ entry_count = EC_READ_U8(data + 2);
+ pdo->sync_index = EC_READ_U8(data + 3);
+ ret = ec_pdo_set_name(pdo,
+ ec_slave_sii_string(slave, EC_READ_U8(data + 5)));
+ if (ret) {
+ ec_pdo_clear(pdo);
+ kfree(pdo);
+ return ret;
+ }
+ list_add_tail(&pdo->list, &slave->sii.pdos);
+
+ data_size -= 8;
+ data += 8;
+
+ for (i = 0; i < entry_count; i++) {
+ if (!(entry = kmalloc(sizeof(ec_pdo_entry_t), GFP_KERNEL))) {
+ EC_SLAVE_ERR(slave, "Failed to allocate PDO entry memory.\n");
+ return -ENOMEM;
+ }
+
+ ec_pdo_entry_init(entry);
+ entry->index = EC_READ_U16(data);
+ entry->subindex = EC_READ_U8(data + 2);
+ ret = ec_pdo_entry_set_name(entry,
+ ec_slave_sii_string(slave, EC_READ_U8(data + 3)));
+ if (ret) {
+ ec_pdo_entry_clear(entry);
+ kfree(entry);
+ return ret;
+ }
+ entry->bit_length = EC_READ_U8(data + 5);
+ list_add_tail(&entry->list, &pdo->entries);
+
+ data_size -= 8;
+ data += 8;
+ }
+
+ // if sync manager index is positive, the PDO is mapped by default
+ if (pdo->sync_index >= 0) {
+ ec_sync_t *sync;
+
+ if (!(sync = ec_slave_get_sync(slave, pdo->sync_index))) {
+ EC_SLAVE_ERR(slave, "Invalid SM index %i for PDO 0x%04X.",
+ pdo->sync_index, pdo->index);
+ return -ENOENT;
+ }
+
+ ret = ec_pdo_list_add_pdo_copy(&sync->pdos, pdo);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/**
+ Searches the string list for an index.
+ \return 0 in case of success, else < 0
+*/
+
+char *ec_slave_sii_string(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ unsigned int index /**< string index */
+ )
+{
+ if (!index--)
+ return NULL;
+
+ if (index >= slave->sii.string_count) {
+ EC_SLAVE_DBG(slave, 1, "String %u not found.\n", index);
+ return NULL;
+ }
+
+ return slave->sii.strings[index];
+}
+
+/*****************************************************************************/
+
+/** Get the sync manager given an index.
+ *
+ * \return pointer to sync manager, or NULL.
+ */
+ec_sync_t *ec_slave_get_sync(
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ uint8_t sync_index /**< Sync manager index. */
+ )
+{
+ if (sync_index < slave->sii.sync_count) {
+ return &slave->sii.syncs[sync_index];
+ } else {
+ return NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/**
+ Counts the total number of SDOs and entries in the dictionary.
+*/
+
+void ec_slave_sdo_dict_info(const ec_slave_t *slave, /**< EtherCAT slave */
+ unsigned int *sdo_count, /**< number of SDOs */
+ unsigned int *entry_count /**< total number of
+ entries */
+ )
+{
+ unsigned int sdos = 0, entries = 0;
+ ec_sdo_t *sdo;
+ ec_sdo_entry_t *entry;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ sdos++;
+ list_for_each_entry(entry, &sdo->entries, list) {
+ entries++;
+ }
+ }
+
+ *sdo_count = sdos;
+ *entry_count = entries;
+}
+
+/*****************************************************************************/
+
+/**
+ * Get an SDO from the dictionary.
+ * \returns The desired SDO, or NULL.
+ */
+
+ec_sdo_t *ec_slave_get_sdo(
+ ec_slave_t *slave, /**< EtherCAT slave */
+ uint16_t index /**< SDO index */
+ )
+{
+ ec_sdo_t *sdo;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ if (sdo->index != index)
+ continue;
+ return sdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/**
+ * Get an SDO from the dictionary.
+ *
+ * const version.
+ *
+ * \returns The desired SDO, or NULL.
+ */
+
+const ec_sdo_t *ec_slave_get_sdo_const(
+ const ec_slave_t *slave, /**< EtherCAT slave */
+ uint16_t index /**< SDO index */
+ )
+{
+ const ec_sdo_t *sdo;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ if (sdo->index != index)
+ continue;
+ return sdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Get an SDO from the dictionary, given its position in the list.
+ * \returns The desired SDO, or NULL.
+ */
+
+const ec_sdo_t *ec_slave_get_sdo_by_pos_const(
+ const ec_slave_t *slave, /**< EtherCAT slave. */
+ uint16_t sdo_position /**< SDO list position. */
+ )
+{
+ const ec_sdo_t *sdo;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ if (sdo_position--)
+ continue;
+ return sdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Get the number of SDOs in the dictionary.
+ * \returns SDO count.
+ */
+
+uint16_t ec_slave_sdo_count(
+ const ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ const ec_sdo_t *sdo;
+ uint16_t count = 0;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Finds a mapped PDO.
+ * \returns The desired PDO object, or NULL.
+ */
+const ec_pdo_t *ec_slave_find_pdo(
+ const ec_slave_t *slave, /**< Slave. */
+ uint16_t index /**< PDO index to find. */
+ )
+{
+ unsigned int i;
+ const ec_sync_t *sync;
+ const ec_pdo_t *pdo;
+
+ for (i = 0; i < slave->sii.sync_count; i++) {
+ sync = &slave->sii.syncs[i];
+
+ if (!(pdo = ec_pdo_list_find_pdo_const(&sync->pdos, index)))
+ continue;
+
+ return pdo;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Find name for a PDO and its entries.
+ */
+void ec_slave_find_names_for_pdo(
+ ec_slave_t *slave,
+ ec_pdo_t *pdo
+ )
+{
+ const ec_sdo_t *sdo;
+ ec_pdo_entry_t *pdo_entry;
+ const ec_sdo_entry_t *sdo_entry;
+
+ list_for_each_entry(sdo, &slave->sdo_dictionary, list) {
+ if (sdo->index == pdo->index) {
+ ec_pdo_set_name(pdo, sdo->name);
+ } else {
+ list_for_each_entry(pdo_entry, &pdo->entries, list) {
+ if (sdo->index == pdo_entry->index) {
+ sdo_entry = ec_sdo_get_entry_const(
+ sdo, pdo_entry->subindex);
+ if (sdo_entry) {
+ ec_pdo_entry_set_name(pdo_entry,
+ sdo_entry->description);
+ }
+ }
+ }
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** Attach PDO names.
+ */
+void ec_slave_attach_pdo_names(
+ ec_slave_t *slave
+ )
+{
+ unsigned int i;
+ ec_sync_t *sync;
+ ec_pdo_t *pdo;
+
+ for (i = 0; i < slave->sii.sync_count; i++) {
+ sync = slave->sii.syncs + i;
+ list_for_each_entry(pdo, &sync->pdos.list, list) {
+ ec_slave_find_names_for_pdo(slave, pdo);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** Returns the previous connected port of a given port.
+ *
+ * \return Port index.
+ */
+unsigned int ec_slave_get_previous_port(
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ unsigned int port_index /**< Port index. */
+ )
+{
+ static const unsigned int prev_table[EC_MAX_PORTS] = {
+ 2, 3, 1, 0
+ };
+
+ if (port_index >= EC_MAX_PORTS) {
+ EC_SLAVE_WARN(slave, "%s(port_index=%u): Invalid port index!\n",
+ __func__, port_index);
+ }
+
+ do {
+ port_index = prev_table[port_index];
+ if (slave->ports[port_index].next_slave) {
+ return port_index;
+ }
+ } while (port_index);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Returns the next connected port of a given port.
+ *
+ * \return Port index.
+ */
+unsigned int ec_slave_get_next_port(
+ ec_slave_t *slave, /**< EtherCAT slave. */
+ unsigned int port_index /**< Port index. */
+ )
+{
+ static const unsigned int next_table[EC_MAX_PORTS] = {
+ 3, 2, 0, 1
+ };
+
+ if (port_index >= EC_MAX_PORTS) {
+ EC_SLAVE_WARN(slave, "%s(port_index=%u): Invalid port index!\n",
+ __func__, port_index);
+ }
+
+ do {
+ port_index = next_table[port_index];
+ if (slave->ports[port_index].next_slave) {
+ return port_index;
+ }
+ } while (port_index);
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Calculates the sum of round-trip-times of connected ports 1-3.
+ *
+ * \return Round-trip-time in ns.
+ */
+uint32_t ec_slave_calc_rtt_sum(
+ ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ uint32_t rtt_sum = 0, rtt;
+ unsigned int port_index = ec_slave_get_next_port(slave, 0);
+
+ while (port_index != 0) {
+ unsigned int prev_index =
+ ec_slave_get_previous_port(slave, port_index);
+
+ rtt = slave->ports[port_index].receive_time -
+ slave->ports[prev_index].receive_time;
+ rtt_sum += rtt;
+ port_index = ec_slave_get_next_port(slave, port_index);
+ }
+
+ return rtt_sum;
+}
+
+/*****************************************************************************/
+
+/** Finds the next slave supporting DC delay measurement.
+ *
+ * \return Next DC slave, or NULL.
+ */
+ec_slave_t *ec_slave_find_next_dc_slave(
+ ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ unsigned int port_index;
+ ec_slave_t *dc_slave = NULL;
+
+ if (slave->base_dc_supported) {
+ dc_slave = slave;
+ } else {
+ port_index = ec_slave_get_next_port(slave, 0);
+
+ while (port_index != 0) {
+ ec_slave_t *next = slave->ports[port_index].next_slave;
+
+ if (next) {
+ dc_slave = ec_slave_find_next_dc_slave(next);
+
+ if (dc_slave) {
+ break;
+ }
+ }
+ port_index = ec_slave_get_next_port(slave, port_index);
+ }
+ }
+
+ return dc_slave;
+}
+
+/*****************************************************************************/
+
+/** Calculates the port transmission delays.
+ */
+void ec_slave_calc_port_delays(
+ ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ unsigned int port_index;
+ ec_slave_t *next_slave, *next_dc;
+ uint32_t rtt, next_rtt_sum;
+
+ if (!slave->base_dc_supported)
+ return;
+
+ port_index = ec_slave_get_next_port(slave, 0);
+
+ while (port_index != 0) {
+ next_slave = slave->ports[port_index].next_slave;
+ next_dc = ec_slave_find_next_dc_slave(next_slave);
+
+ if (next_dc) {
+ unsigned int prev_port =
+ ec_slave_get_previous_port(slave, port_index);
+
+ rtt = slave->ports[port_index].receive_time -
+ slave->ports[prev_port].receive_time;
+ next_rtt_sum = ec_slave_calc_rtt_sum(next_dc);
+
+ slave->ports[port_index].delay_to_next_dc =
+ (rtt - next_rtt_sum) / 2; // FIXME
+ next_dc->ports[0].delay_to_next_dc =
+ (rtt - next_rtt_sum) / 2;
+
+#if 0
+ EC_SLAVE_DBG(slave, 1, "delay %u:%u rtt=%u"
+ " next_rtt_sum=%u delay=%u\n",
+ slave->ring_position, port_index, rtt, next_rtt_sum,
+ slave->ports[port_index].delay_to_next_dc);
+#endif
+ }
+
+ port_index = ec_slave_get_next_port(slave, port_index);
+ }
+}
+
+/*****************************************************************************/
+
+/** Recursively calculates transmission delays.
+ */
+void ec_slave_calc_transmission_delays_rec(
+ ec_slave_t *slave, /**< Current slave. */
+ uint32_t *delay /**< Sum of delays. */
+ )
+{
+ unsigned int i;
+ ec_slave_t *next_dc;
+
+ EC_SLAVE_DBG(slave, 1, "%s(delay = %u ns)\n", __func__, *delay);
+
+ slave->transmission_delay = *delay;
+
+ i = ec_slave_get_next_port(slave, 0);
+
+ while (i != 0) {
+ ec_slave_port_t *port = &slave->ports[i];
+ next_dc = ec_slave_find_next_dc_slave(port->next_slave);
+ if (next_dc) {
+ *delay = *delay + port->delay_to_next_dc;
+#if 0
+ EC_SLAVE_DBG(slave, 1, "%u:%u %u\n",
+ slave->ring_position, i, *delay);
+#endif
+ ec_slave_calc_transmission_delays_rec(next_dc, delay);
+ }
+
+ i = ec_slave_get_next_port(slave, i);
+ }
+
+ *delay = *delay + slave->ports[0].delay_to_next_dc;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/slave.h b/net/ethercat/master/slave.h
new file mode 100644
index 000000000000..b71d57e28208
--- /dev/null
+++ b/net/ethercat/master/slave.h
@@ -0,0 +1,273 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 slave structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SLAVE_H__
+#define __EC_SLAVE_H__
+
+#include <linux/list.h>
+#include <linux/kobject.h>
+
+#include "globals.h"
+#include "datagram.h"
+#include "pdo.h"
+#include "sync.h"
+#include "sdo.h"
+#include "fsm_slave.h"
+
+/*****************************************************************************/
+
+/** Convenience macro for printing slave-specific information to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX>-<POSITION>: ", where INDEX is the master index and
+ * POSITION is the slave's ring position.
+ *
+ * \param slave EtherCAT slave
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_SLAVE_INFO(slave, fmt, args...) \
+ printk(KERN_INFO "EtherCAT %u-%u: " fmt, slave->master->index, \
+ slave->ring_position, ##args)
+
+/** Convenience macro for printing slave-specific errors to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX>-<POSITION>: ", where INDEX is the master index and
+ * POSITION is the slave's ring position.
+ *
+ * \param slave EtherCAT slave
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_SLAVE_ERR(slave, fmt, args...) \
+ printk(KERN_ERR "EtherCAT ERROR %u-%u: " fmt, slave->master->index, \
+ slave->ring_position, ##args)
+
+/** Convenience macro for printing slave-specific warnings to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX>-<POSITION>: ", where INDEX is the master index and
+ * POSITION is the slave's ring position.
+ *
+ * \param slave EtherCAT slave
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_SLAVE_WARN(slave, fmt, args...) \
+ printk(KERN_WARNING "EtherCAT WARNING %u-%u: " fmt, \
+ slave->master->index, slave->ring_position, ##args)
+
+/** Convenience macro for printing slave-specific debug messages to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX>-<POSITION>: ", where INDEX is the master index and
+ * POSITION is the slave's ring position.
+ *
+ * \param slave EtherCAT slave
+ * \param level Debug level. Master's debug level must be >= \a level for
+ * output.
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_SLAVE_DBG(slave, level, fmt, args...) \
+ do { \
+ if (slave->master->debug_level >= level) { \
+ printk(KERN_DEBUG "EtherCAT DEBUG %u-%u: " fmt, \
+ slave->master->index, slave->ring_position, ##args); \
+ } \
+ } while (0)
+
+/*****************************************************************************/
+
+/** Slave port.
+ */
+typedef struct {
+ ec_slave_port_desc_t desc; /**< Port descriptors. */
+ ec_slave_port_link_t link; /**< Port link status. */
+ ec_slave_t *next_slave; /**< Connected slaves. */
+ uint32_t receive_time; /**< Port receive times for delay
+ measurement. */
+ uint32_t delay_to_next_dc; /**< Delay to next slave with DC support behind
+ this port [ns]. */
+} ec_slave_port_t;
+
+/*****************************************************************************/
+
+/** Slave information interface data.
+ */
+typedef struct {
+ // Non-category data
+ uint16_t alias; /**< Configured station alias. */
+ uint32_t vendor_id; /**< Vendor ID. */
+ uint32_t product_code; /**< Vendor-specific product code. */
+ uint32_t revision_number; /**< Revision number. */
+ uint32_t serial_number; /**< Serial number. */
+ uint16_t boot_rx_mailbox_offset; /**< Bootstrap receive mailbox address. */
+ uint16_t boot_rx_mailbox_size; /**< Bootstrap receive mailbox size. */
+ uint16_t boot_tx_mailbox_offset; /**< Bootstrap transmit mailbox address. */
+ uint16_t boot_tx_mailbox_size; /**< Bootstrap transmit mailbox size. */
+ uint16_t std_rx_mailbox_offset; /**< Standard receive mailbox address. */
+ uint16_t std_rx_mailbox_size; /**< Standard receive mailbox size. */
+ uint16_t std_tx_mailbox_offset; /**< Standard transmit mailbox address. */
+ uint16_t std_tx_mailbox_size; /**< Standard transmit mailbox size. */
+ uint16_t mailbox_protocols; /**< Supported mailbox protocols. */
+
+ // Strings
+ char **strings; /**< Strings in SII categories. */
+ unsigned int string_count; /**< Number of SII strings. */
+
+ // General
+ unsigned int has_general; /**< General category present. */
+ char *group; /**< Group name. */
+ char *image; /**< Image name. */
+ char *order; /**< Order number. */
+ char *name; /**< Slave name. */
+ uint8_t physical_layer[EC_MAX_PORTS]; /**< Port media. */
+ ec_sii_coe_details_t coe_details; /**< CoE detail flags. */
+ ec_sii_general_flags_t general_flags; /**< General flags. */
+ int16_t current_on_ebus; /**< Power consumption in mA. */
+
+ // SyncM
+ ec_sync_t *syncs; /**< SYNC MANAGER categories. */
+ unsigned int sync_count; /**< Number of sync managers. */
+
+ // [RT]XPDO
+ struct list_head pdos; /**< SII [RT]XPDO categories. */
+} ec_sii_t;
+
+/*****************************************************************************/
+
+/** EtherCAT slave.
+ */
+struct ec_slave
+{
+ ec_master_t *master; /**< Master owning the slave. */
+ ec_device_index_t device_index; /**< Index of device the slave responds
+ on. */
+
+ // addresses
+ uint16_t ring_position; /**< Ring position. */
+ uint16_t station_address; /**< Configured station address. */
+ uint16_t effective_alias; /**< Effective alias address. */
+
+ ec_slave_port_t ports[EC_MAX_PORTS]; /**< Ports. */
+
+ // configuration
+ ec_slave_config_t *config; /**< Current configuration. */
+ ec_slave_state_t requested_state; /**< Requested application state. */
+ ec_slave_state_t current_state; /**< Current application state. */
+ unsigned int error_flag; /**< Stop processing after an error. */
+ unsigned int force_config; /**< Force (re-)configuration. */
+ uint16_t configured_rx_mailbox_offset; /**< Configured receive mailbox
+ offset. */
+ uint16_t configured_rx_mailbox_size; /**< Configured receive mailbox size.
+ */
+ uint16_t configured_tx_mailbox_offset; /**< Configured send mailbox
+ offset. */
+ uint16_t configured_tx_mailbox_size; /**< Configured send mailbox size. */
+
+ // base data
+ uint8_t base_type; /**< Slave type. */
+ uint8_t base_revision; /**< Revision. */
+ uint16_t base_build; /**< Build number. */
+ uint8_t base_fmmu_count; /**< Number of supported FMMUs. */
+ uint8_t base_sync_count; /**< Number of supported sync managers. */
+ uint8_t base_fmmu_bit_operation; /**< FMMU bit operation is supported. */
+ uint8_t base_dc_supported; /**< Distributed clocks are supported. */
+ ec_slave_dc_range_t base_dc_range; /**< DC range. */
+ uint8_t has_dc_system_time; /**< The slave supports the DC system time
+ register. Otherwise it can only be used for
+ delay measurement. */
+ uint32_t transmission_delay; /**< DC system time transmission delay
+ (offset from reference clock). */
+
+ // SII
+ uint16_t *sii_words; /**< Complete SII image. */
+ size_t sii_nwords; /**< Size of the SII contents in words. */
+
+ // Slave information interface
+ ec_sii_t sii; /**< Extracted SII data. */
+
+ struct list_head sdo_dictionary; /**< SDO dictionary list */
+ uint8_t sdo_dictionary_fetched; /**< Dictionary has been fetched. */
+ unsigned long jiffies_preop; /**< Time, the slave went to PREOP. */
+
+ 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. */
+
+ ec_fsm_slave_t fsm; /**< Slave state machine. */
+};
+
+/*****************************************************************************/
+
+// slave construction/destruction
+void ec_slave_init(ec_slave_t *, ec_master_t *, ec_device_index_t,
+ uint16_t, uint16_t);
+void ec_slave_clear(ec_slave_t *);
+
+void ec_slave_clear_sync_managers(ec_slave_t *);
+
+void ec_slave_request_state(ec_slave_t *, ec_slave_state_t);
+void ec_slave_set_state(ec_slave_t *, ec_slave_state_t);
+
+// SII categories
+int ec_slave_fetch_sii_strings(ec_slave_t *, const uint8_t *, size_t);
+int ec_slave_fetch_sii_general(ec_slave_t *, const uint8_t *, size_t);
+int ec_slave_fetch_sii_syncs(ec_slave_t *, const uint8_t *, size_t);
+int ec_slave_fetch_sii_pdos(ec_slave_t *, const uint8_t *, size_t,
+ ec_direction_t);
+
+// misc.
+ec_sync_t *ec_slave_get_sync(ec_slave_t *, uint8_t);
+
+void ec_slave_sdo_dict_info(const ec_slave_t *,
+ unsigned int *, unsigned int *);
+ec_sdo_t *ec_slave_get_sdo(ec_slave_t *, uint16_t);
+const ec_sdo_t *ec_slave_get_sdo_const(const ec_slave_t *, uint16_t);
+const ec_sdo_t *ec_slave_get_sdo_by_pos_const(const ec_slave_t *, uint16_t);
+uint16_t ec_slave_sdo_count(const ec_slave_t *);
+const ec_pdo_t *ec_slave_find_pdo(const ec_slave_t *, uint16_t);
+void ec_slave_attach_pdo_names(ec_slave_t *);
+
+void ec_slave_calc_port_delays(ec_slave_t *);
+void ec_slave_calc_transmission_delays_rec(ec_slave_t *, uint32_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/slave_config.c b/net/ethercat/master/slave_config.c
new file mode 100644
index 000000000000..cf5d501f9f0c
--- /dev/null
+++ b/net/ethercat/master/slave_config.c
@@ -0,0 +1,1269 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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
+ *
+ *****************************************************************************/
+
+/**
+ \file
+ EtherCAT slave configuration methods.
+*/
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "globals.h"
+#include "master.h"
+#include "voe_handler.h"
+
+#include "slave_config.h"
+
+/*****************************************************************************/
+
+/** Slave configuration constructor.
+ *
+ * See ecrt_master_slave_config() for the usage of the \a alias and \a
+ * position parameters.
+ */
+void ec_slave_config_init(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ ec_master_t *master, /**< EtherCAT master. */
+ uint16_t alias, /**< Slave alias. */
+ uint16_t position, /**< Slave position. */
+ uint32_t vendor_id, /**< Expected vendor ID. */
+ uint32_t product_code /**< Expected product code. */
+ )
+{
+ unsigned int i;
+
+ sc->master = master;
+
+ sc->alias = alias;
+ sc->position = position;
+ sc->vendor_id = vendor_id;
+ sc->product_code = product_code;
+ sc->watchdog_divider = 0; // use default
+ sc->watchdog_intervals = 0; // use default
+
+ sc->slave = NULL;
+
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++)
+ ec_sync_config_init(&sc->sync_configs[i]);
+
+ sc->used_fmmus = 0;
+ sc->dc_assign_activate = 0x0000;
+ sc->dc_sync[0].cycle_time = 0U;
+ sc->dc_sync[1].cycle_time = 0;
+ sc->dc_sync[0].shift_time = 0U;
+ sc->dc_sync[1].shift_time = 0;
+
+ INIT_LIST_HEAD(&sc->sdo_configs);
+ INIT_LIST_HEAD(&sc->sdo_requests);
+ INIT_LIST_HEAD(&sc->reg_requests);
+ INIT_LIST_HEAD(&sc->voe_handlers);
+ INIT_LIST_HEAD(&sc->soe_configs);
+
+ ec_coe_emerg_ring_init(&sc->emerg_ring, sc);
+}
+
+/*****************************************************************************/
+
+/** Slave configuration destructor.
+ *
+ * Clears and frees a slave configuration object.
+ */
+void ec_slave_config_clear(
+ ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ unsigned int i;
+ ec_sdo_request_t *req, *next_req;
+ ec_voe_handler_t *voe, *next_voe;
+ ec_reg_request_t *reg, *next_reg;
+ ec_soe_request_t *soe, *next_soe;
+
+ ec_slave_config_detach(sc);
+
+ // Free sync managers
+ for (i = 0; i < EC_MAX_SYNC_MANAGERS; i++)
+ ec_sync_config_clear(&sc->sync_configs[i]);
+
+ // free all SDO configurations
+ list_for_each_entry_safe(req, next_req, &sc->sdo_configs, list) {
+ list_del(&req->list);
+ ec_sdo_request_clear(req);
+ kfree(req);
+ }
+
+ // free all SDO requests
+ list_for_each_entry_safe(req, next_req, &sc->sdo_requests, list) {
+ list_del(&req->list);
+ ec_sdo_request_clear(req);
+ kfree(req);
+ }
+
+ // free all register requests
+ list_for_each_entry_safe(reg, next_reg, &sc->reg_requests, list) {
+ list_del(&reg->list);
+ ec_reg_request_clear(reg);
+ kfree(reg);
+ }
+
+ // free all VoE handlers
+ list_for_each_entry_safe(voe, next_voe, &sc->voe_handlers, list) {
+ list_del(&voe->list);
+ ec_voe_handler_clear(voe);
+ kfree(voe);
+ }
+
+ // free all SoE configurations
+ list_for_each_entry_safe(soe, next_soe, &sc->soe_configs, list) {
+ list_del(&soe->list);
+ ec_soe_request_clear(soe);
+ kfree(soe);
+ }
+
+ ec_coe_emerg_ring_clear(&sc->emerg_ring);
+}
+
+/*****************************************************************************/
+
+/** Prepares an FMMU configuration.
+ *
+ * Configuration data for the FMMU is saved in the slave config structure and
+ * is written to the slave during the configuration. The FMMU configuration
+ * is done in a way, that the complete data range of the corresponding sync
+ * manager is covered. Seperate FMMUs are configured for each domain. If the
+ * FMMU configuration is already prepared, the function does nothing and
+ * returns with success.
+ *
+ * \retval >=0 Success, logical offset byte address.
+ * \retval <0 Error code.
+ */
+int ec_slave_config_prepare_fmmu(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ ec_domain_t *domain, /**< Domain. */
+ uint8_t sync_index, /**< Sync manager index. */
+ ec_direction_t dir /**< PDO direction. */
+ )
+{
+ unsigned int i;
+ ec_fmmu_config_t *fmmu;
+
+ // FMMU configuration already prepared?
+ for (i = 0; i < sc->used_fmmus; i++) {
+ fmmu = &sc->fmmu_configs[i];
+ if (fmmu->domain == domain && fmmu->sync_index == sync_index)
+ return fmmu->logical_start_address;
+ }
+
+ if (sc->used_fmmus == EC_MAX_FMMUS) {
+ EC_CONFIG_ERR(sc, "FMMU limit reached!\n");
+ return -EOVERFLOW;
+ }
+
+ fmmu = &sc->fmmu_configs[sc->used_fmmus++];
+
+ down(&sc->master->master_sem);
+ ec_fmmu_config_init(fmmu, sc, domain, sync_index, dir);
+ up(&sc->master->master_sem);
+
+ return fmmu->logical_start_address;
+}
+
+/*****************************************************************************/
+
+/** Attaches the configuration to the addressed slave object.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_slave_config_attach(
+ ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ ec_slave_t *slave;
+
+ if (sc->slave)
+ return 0; // already attached
+
+ if (!(slave = ec_master_find_slave(
+ sc->master, sc->alias, sc->position))) {
+ EC_CONFIG_DBG(sc, 1, "Failed to find slave for configuration.\n");
+ return -ENOENT;
+ }
+
+ if (slave->config) {
+ EC_CONFIG_DBG(sc, 1, "Failed to attach configuration. Slave %u"
+ " already has a configuration!\n", slave->ring_position);
+ return -EEXIST;
+ }
+
+ if (
+#ifdef EC_IDENT_WILDCARDS
+ sc->vendor_id != 0xffffffff &&
+#endif
+ slave->sii.vendor_id != sc->vendor_id
+ ) {
+ EC_CONFIG_DBG(sc, 1, "Slave %u has no matching vendor ID (0x%08X)"
+ " for configuration (0x%08X).\n",
+ slave->ring_position, slave->sii.vendor_id, sc->vendor_id);
+ return -EINVAL;
+ }
+
+ if (
+#ifdef EC_IDENT_WILDCARDS
+ sc->product_code != 0xffffffff &&
+#endif
+ slave->sii.product_code != sc->product_code
+ ) {
+ EC_CONFIG_DBG(sc, 1, "Slave %u has no matching product code (0x%08X)"
+ " for configuration (0x%08X).\n",
+ slave->ring_position, slave->sii.product_code,
+ sc->product_code);
+ return -EINVAL;
+ }
+
+ // attach slave
+ slave->config = sc;
+ sc->slave = slave;
+
+ EC_CONFIG_DBG(sc, 1, "Attached slave %u.\n", slave->ring_position);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Detaches the configuration from a slave object.
+ */
+void ec_slave_config_detach(
+ ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ if (sc->slave) {
+ ec_reg_request_t *reg;
+
+ sc->slave->config = NULL;
+
+ // invalidate processing register request
+ list_for_each_entry(reg, &sc->reg_requests, list) {
+ if (sc->slave->fsm.reg_request == reg) {
+ sc->slave->fsm.reg_request = NULL;
+ break;
+ }
+ }
+
+ sc->slave = NULL;
+ }
+}
+
+/*****************************************************************************/
+
+/** Loads the default PDO assignment from the slave object.
+ */
+void ec_slave_config_load_default_sync_config(ec_slave_config_t *sc)
+{
+ uint8_t sync_index;
+ ec_sync_config_t *sync_config;
+ const ec_sync_t *sync;
+
+ if (!sc->slave)
+ return;
+
+ for (sync_index = 0; sync_index < EC_MAX_SYNC_MANAGERS; sync_index++) {
+ sync_config = &sc->sync_configs[sync_index];
+ if ((sync = ec_slave_get_sync(sc->slave, sync_index))) {
+ sync_config->dir = ec_sync_default_direction(sync);
+ if (sync_config->dir == EC_DIR_INVALID)
+ EC_SLAVE_WARN(sc->slave,
+ "SM%u has an invalid direction field!\n", sync_index);
+ ec_pdo_list_copy(&sync_config->pdos, &sync->pdos);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/** Loads the default mapping for a PDO from the slave object.
+ */
+void ec_slave_config_load_default_mapping(
+ const ec_slave_config_t *sc,
+ ec_pdo_t *pdo
+ )
+{
+ unsigned int i;
+ const ec_sync_t *sync;
+ const ec_pdo_t *default_pdo;
+
+ if (!sc->slave)
+ return;
+
+ EC_CONFIG_DBG(sc, 1, "Loading default mapping for PDO 0x%04X.\n",
+ pdo->index);
+
+ // find PDO in any sync manager (it could be reassigned later)
+ for (i = 0; i < sc->slave->sii.sync_count; i++) {
+ sync = &sc->slave->sii.syncs[i];
+
+ list_for_each_entry(default_pdo, &sync->pdos.list, list) {
+ if (default_pdo->index != pdo->index)
+ continue;
+
+ if (default_pdo->name) {
+ EC_CONFIG_DBG(sc, 1, "Found PDO name \"%s\".\n",
+ default_pdo->name);
+
+ // take PDO name from assigned one
+ ec_pdo_set_name(pdo, default_pdo->name);
+ }
+
+ // copy entries (= default PDO mapping)
+ if (ec_pdo_copy_entries(pdo, default_pdo))
+ return;
+
+ if (sc->master->debug_level) {
+ const ec_pdo_entry_t *entry;
+ list_for_each_entry(entry, &pdo->entries, list) {
+ EC_CONFIG_DBG(sc, 1, "Entry 0x%04X:%02X.\n",
+ entry->index, entry->subindex);
+ }
+ }
+
+ return;
+ }
+ }
+
+ EC_CONFIG_DBG(sc, 1, "No default mapping found.\n");
+}
+
+/*****************************************************************************/
+
+/** Get the number of SDO configurations.
+ *
+ * \return Number of SDO configurations.
+ */
+unsigned int ec_slave_config_sdo_count(
+ const ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ const ec_sdo_request_t *req;
+ unsigned int count = 0;
+
+ list_for_each_entry(req, &sc->sdo_configs, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Finds an SDO configuration via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Search result, or NULL.
+ */
+const ec_sdo_request_t *ec_slave_config_get_sdo_by_pos_const(
+ const ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ const ec_sdo_request_t *req;
+
+ list_for_each_entry(req, &sc->sdo_configs, list) {
+ if (pos--)
+ continue;
+ return req;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Get the number of IDN configurations.
+ *
+ * \return Number of SDO configurations.
+ */
+unsigned int ec_slave_config_idn_count(
+ const ec_slave_config_t *sc /**< Slave configuration. */
+ )
+{
+ const ec_soe_request_t *req;
+ unsigned int count = 0;
+
+ list_for_each_entry(req, &sc->soe_configs, list) {
+ count++;
+ }
+
+ return count;
+}
+
+/*****************************************************************************/
+
+/** Finds an IDN configuration via its position in the list.
+ *
+ * Const version.
+ *
+ * \return Search result, or NULL.
+ */
+const ec_soe_request_t *ec_slave_config_get_idn_by_pos_const(
+ const ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ const ec_soe_request_t *req;
+
+ list_for_each_entry(req, &sc->soe_configs, list) {
+ if (pos--)
+ continue;
+ return req;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Finds a CoE handler via its position in the list.
+ *
+ * \return Search result, or NULL.
+ */
+ec_sdo_request_t *ec_slave_config_find_sdo_request(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ ec_sdo_request_t *req;
+
+ list_for_each_entry(req, &sc->sdo_requests, list) {
+ if (pos--)
+ continue;
+ return req;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Finds a register handler via its position in the list.
+ *
+ * \return Search result, or NULL.
+ */
+ec_reg_request_t *ec_slave_config_find_reg_request(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ ec_reg_request_t *reg;
+
+ list_for_each_entry(reg, &sc->reg_requests, list) {
+ if (pos--)
+ continue;
+ return reg;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
+/** Finds a VoE handler via its position in the list.
+ *
+ * \return Search result, or NULL.
+ */
+ec_voe_handler_t *ec_slave_config_find_voe_handler(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ ec_voe_handler_t *voe;
+
+ list_for_each_entry(voe, &sc->voe_handlers, list) {
+ if (pos--)
+ continue;
+ return voe;
+ }
+
+ return NULL;
+}
+
+/******************************************************************************
+ * Application interface
+ *****************************************************************************/
+
+int ecrt_slave_config_sync_manager(ec_slave_config_t *sc, uint8_t sync_index,
+ ec_direction_t dir, ec_watchdog_mode_t watchdog_mode)
+{
+ ec_sync_config_t *sync_config;
+
+ EC_CONFIG_DBG(sc, 1, "ecrt_slave_config_sync_manager(sc = 0x%p,"
+ " sync_index = %u, dir = %i, watchdog_mode = %i)\n",
+ sc, sync_index, dir, watchdog_mode);
+
+ if (sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_CONFIG_ERR(sc, "Invalid sync manager index %u!\n", sync_index);
+ return -ENOENT;
+ }
+
+ if (dir != EC_DIR_OUTPUT && dir != EC_DIR_INPUT) {
+ EC_CONFIG_ERR(sc, "Invalid direction %u!\n", (unsigned int) dir);
+ return -EINVAL;
+ }
+
+ sync_config = &sc->sync_configs[sync_index];
+ sync_config->dir = dir;
+ sync_config->watchdog_mode = watchdog_mode;
+ return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_slave_config_watchdog(ec_slave_config_t *sc,
+ uint16_t divider, uint16_t intervals)
+{
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, divider = %u, intervals = %u)\n",
+ __func__, sc, divider, intervals);
+
+ sc->watchdog_divider = divider;
+ sc->watchdog_intervals = intervals;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_pdo_assign_add(ec_slave_config_t *sc,
+ uint8_t sync_index, uint16_t pdo_index)
+{
+ ec_pdo_t *pdo;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, sync_index = %u, "
+ "pdo_index = 0x%04X)\n", __func__, sc, sync_index, pdo_index);
+
+ if (sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_CONFIG_ERR(sc, "Invalid sync manager index %u!\n", sync_index);
+ return -EINVAL;
+ }
+
+ down(&sc->master->master_sem);
+
+ pdo = ec_pdo_list_add_pdo(&sc->sync_configs[sync_index].pdos, pdo_index);
+ if (IS_ERR(pdo)) {
+ up(&sc->master->master_sem);
+ return PTR_ERR(pdo);
+ }
+ pdo->sync_index = sync_index;
+
+ ec_slave_config_load_default_mapping(sc, pdo);
+
+ up(&sc->master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+void ecrt_slave_config_pdo_assign_clear(ec_slave_config_t *sc,
+ uint8_t sync_index)
+{
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, sync_index = %u)\n",
+ __func__, sc, sync_index);
+
+ if (sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_CONFIG_ERR(sc, "Invalid sync manager index %u!\n", sync_index);
+ return;
+ }
+
+ down(&sc->master->master_sem);
+ ec_pdo_list_clear_pdos(&sc->sync_configs[sync_index].pdos);
+ up(&sc->master->master_sem);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_pdo_mapping_add(ec_slave_config_t *sc,
+ uint16_t pdo_index, uint16_t entry_index, uint8_t entry_subindex,
+ uint8_t entry_bit_length)
+{
+ uint8_t sync_index;
+ ec_pdo_t *pdo = NULL;
+ ec_pdo_entry_t *entry;
+ int retval = 0;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, "
+ "pdo_index = 0x%04X, entry_index = 0x%04X, "
+ "entry_subindex = 0x%02X, entry_bit_length = %u)\n",
+ __func__, sc, pdo_index, entry_index, entry_subindex,
+ entry_bit_length);
+
+ for (sync_index = 0; sync_index < EC_MAX_SYNC_MANAGERS; sync_index++)
+ if ((pdo = ec_pdo_list_find_pdo(
+ &sc->sync_configs[sync_index].pdos, pdo_index)))
+ break;
+
+ if (pdo) {
+ down(&sc->master->master_sem);
+ entry = ec_pdo_add_entry(pdo, entry_index, entry_subindex,
+ entry_bit_length);
+ up(&sc->master->master_sem);
+ if (IS_ERR(entry))
+ retval = PTR_ERR(entry);
+ } else {
+ EC_CONFIG_ERR(sc, "PDO 0x%04X is not assigned.\n", pdo_index);
+ retval = -ENOENT;
+ }
+
+ return retval;
+}
+
+/*****************************************************************************/
+
+void ecrt_slave_config_pdo_mapping_clear(ec_slave_config_t *sc,
+ uint16_t pdo_index)
+{
+ uint8_t sync_index;
+ ec_pdo_t *pdo = NULL;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, pdo_index = 0x%04X)\n",
+ __func__, sc, pdo_index);
+
+ for (sync_index = 0; sync_index < EC_MAX_SYNC_MANAGERS; sync_index++)
+ if ((pdo = ec_pdo_list_find_pdo(
+ &sc->sync_configs[sync_index].pdos, pdo_index)))
+ break;
+
+ if (pdo) {
+ down(&sc->master->master_sem);
+ ec_pdo_clear_entries(pdo);
+ up(&sc->master->master_sem);
+ } else {
+ EC_CONFIG_WARN(sc, "PDO 0x%04X is not assigned.\n", pdo_index);
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_pdos(ec_slave_config_t *sc,
+ unsigned int n_syncs, const ec_sync_info_t syncs[])
+{
+ int ret;
+ unsigned int i, j, k;
+ const ec_sync_info_t *sync_info;
+ const ec_pdo_info_t *pdo_info;
+ const ec_pdo_entry_info_t *entry_info;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, n_syncs = %u, syncs = 0x%p)\n",
+ __func__, sc, n_syncs, syncs);
+
+ if (!syncs)
+ return 0;
+
+ for (i = 0; i < n_syncs; i++) {
+ sync_info = &syncs[i];
+
+ if (sync_info->index == (uint8_t) EC_END)
+ break;
+
+ if (sync_info->index >= EC_MAX_SYNC_MANAGERS) {
+ EC_CONFIG_ERR(sc, "Invalid sync manager index %u!\n",
+ sync_info->index);
+ return -ENOENT;
+ }
+
+ ret = ecrt_slave_config_sync_manager(sc, sync_info->index,
+ sync_info->dir, sync_info->watchdog_mode);
+ if (ret)
+ return ret;
+
+ ecrt_slave_config_pdo_assign_clear(sc, sync_info->index);
+
+ if (sync_info->n_pdos && sync_info->pdos) {
+
+ for (j = 0; j < sync_info->n_pdos; j++) {
+ pdo_info = &sync_info->pdos[j];
+
+ ret = ecrt_slave_config_pdo_assign_add(
+ sc, sync_info->index, pdo_info->index);
+ if (ret)
+ return ret;
+
+ ecrt_slave_config_pdo_mapping_clear(sc, pdo_info->index);
+
+ if (pdo_info->n_entries && pdo_info->entries) {
+ for (k = 0; k < pdo_info->n_entries; k++) {
+ entry_info = &pdo_info->entries[k];
+
+ ret = ecrt_slave_config_pdo_mapping_add(sc,
+ pdo_info->index, entry_info->index,
+ entry_info->subindex,
+ entry_info->bit_length);
+ if (ret)
+ return ret;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_reg_pdo_entry(
+ ec_slave_config_t *sc,
+ uint16_t index,
+ uint8_t subindex,
+ ec_domain_t *domain,
+ unsigned int *bit_position
+ )
+{
+ uint8_t sync_index;
+ const ec_sync_config_t *sync_config;
+ unsigned int bit_offset, bit_pos;
+ ec_pdo_t *pdo;
+ ec_pdo_entry_t *entry;
+ int sync_offset;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "subindex = 0x%02X, domain = 0x%p, bit_position = 0x%p)\n",
+ __func__, sc, index, subindex, domain, bit_position);
+
+ for (sync_index = 0; sync_index < EC_MAX_SYNC_MANAGERS; sync_index++) {
+ sync_config = &sc->sync_configs[sync_index];
+ bit_offset = 0;
+
+ list_for_each_entry(pdo, &sync_config->pdos.list, list) {
+ list_for_each_entry(entry, &pdo->entries, list) {
+ if (entry->index != index || entry->subindex != subindex) {
+ bit_offset += entry->bit_length;
+ } else {
+ bit_pos = bit_offset % 8;
+ if (bit_position) {
+ *bit_position = bit_pos;
+ } else if (bit_pos) {
+ EC_CONFIG_ERR(sc, "PDO entry 0x%04X:%02X does"
+ " not byte-align.\n", index, subindex);
+ return -EFAULT;
+ }
+
+ sync_offset = ec_slave_config_prepare_fmmu(
+ sc, domain, sync_index, sync_config->dir);
+ if (sync_offset < 0)
+ return sync_offset;
+
+ return sync_offset + bit_offset / 8;
+ }
+ }
+ }
+ }
+
+ EC_CONFIG_ERR(sc, "PDO entry 0x%04X:%02X is not mapped.\n",
+ index, subindex);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_reg_pdo_entry_pos(
+ ec_slave_config_t *sc,
+ uint8_t sync_index,
+ unsigned int pdo_pos,
+ unsigned int entry_pos,
+ ec_domain_t *domain,
+ unsigned int *bit_position
+ )
+{
+ const ec_sync_config_t *sync_config;
+ unsigned int bit_offset, pp, ep;
+ ec_pdo_t *pdo;
+ ec_pdo_entry_t *entry;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, sync_index = %u, pdo_pos = %u,"
+ " entry_pos = %u, domain = 0x%p, bit_position = 0x%p)\n",
+ __func__, sc, sync_index, pdo_pos, entry_pos,
+ domain, bit_position);
+
+ if (sync_index >= EC_MAX_SYNC_MANAGERS) {
+ EC_CONFIG_ERR(sc, "Invalid syncmanager position %u.\n", sync_index);
+ return -EINVAL;
+ }
+
+ sync_config = &sc->sync_configs[sync_index];
+ bit_offset = 0;
+ pp = 0;
+
+ list_for_each_entry(pdo, &sync_config->pdos.list, list) {
+ ep = 0;
+ list_for_each_entry(entry, &pdo->entries, list) {
+ if (pp != pdo_pos || ep != entry_pos) {
+ bit_offset += entry->bit_length;
+ } else {
+ unsigned int bit_pos = bit_offset % 8;
+ int sync_offset;
+
+ if (bit_position) {
+ *bit_position = bit_pos;
+ } else if (bit_pos) {
+ EC_CONFIG_ERR(sc, "PDO entry 0x%04X:%02X does"
+ " not byte-align.\n",
+ pdo->index, entry->subindex);
+ return -EFAULT;
+ }
+
+ sync_offset = ec_slave_config_prepare_fmmu(
+ sc, domain, sync_index, sync_config->dir);
+ if (sync_offset < 0)
+ return sync_offset;
+
+ return sync_offset + bit_offset / 8;
+ }
+ ep++;
+ }
+ pp++;
+ }
+
+ EC_CONFIG_ERR(sc, "PDO entry specification %u/%u/%u out of range.\n",
+ sync_index, pdo_pos, entry_pos);
+ return -ENOENT;
+}
+
+/*****************************************************************************/
+
+void ecrt_slave_config_dc(ec_slave_config_t *sc, uint16_t assign_activate,
+ uint32_t sync0_cycle_time, int32_t sync0_shift_time,
+ uint32_t sync1_cycle_time, int32_t sync1_shift_time)
+{
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, assign_activate = 0x%04X,"
+ " sync0_cycle = %u, sync0_shift = %i,"
+ " sync1_cycle = %u, sync1_shift = %i\n",
+ __func__, sc, assign_activate, sync0_cycle_time, sync0_shift_time,
+ sync1_cycle_time, sync1_shift_time);
+
+ sc->dc_assign_activate = assign_activate;
+ sc->dc_sync[0].cycle_time = sync0_cycle_time;
+ sc->dc_sync[0].shift_time = sync0_shift_time;
+ sc->dc_sync[1].cycle_time = sync1_cycle_time;
+ sc->dc_sync[1].shift_time = sync1_shift_time;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_sdo(ec_slave_config_t *sc, uint16_t index,
+ uint8_t subindex, const uint8_t *data, size_t size)
+{
+ ec_slave_t *slave = sc->slave;
+ ec_sdo_request_t *req;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "subindex = 0x%02X, data = 0x%p, size = %zu)\n",
+ __func__, sc, index, subindex, data, size);
+
+ if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
+ EC_CONFIG_WARN(sc, "Attached slave does not support CoE!\n");
+ }
+
+ if (!(req = (ec_sdo_request_t *)
+ kmalloc(sizeof(ec_sdo_request_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate memory for"
+ " SDO configuration!\n");
+ return -ENOMEM;
+ }
+
+ ec_sdo_request_init(req);
+ ecrt_sdo_request_index(req, index, subindex);
+
+ ret = ec_sdo_request_copy_data(req, data, size);
+ if (ret < 0) {
+ ec_sdo_request_clear(req);
+ kfree(req);
+ return ret;
+ }
+
+ down(&sc->master->master_sem);
+ list_add_tail(&req->list, &sc->sdo_configs);
+ up(&sc->master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_sdo8(ec_slave_config_t *sc, uint16_t index,
+ uint8_t subindex, uint8_t value)
+{
+ uint8_t data[1];
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "subindex = 0x%02X, value = %u)\n",
+ __func__, sc, index, subindex, (unsigned int) value);
+
+ EC_WRITE_U8(data, value);
+ return ecrt_slave_config_sdo(sc, index, subindex, data, 1);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_sdo16(ec_slave_config_t *sc, uint16_t index,
+ uint8_t subindex, uint16_t value)
+{
+ uint8_t data[2];
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "subindex = 0x%02X, value = %u)\n",
+ __func__, sc, index, subindex, value);
+
+ EC_WRITE_U16(data, value);
+ return ecrt_slave_config_sdo(sc, index, subindex, data, 2);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_sdo32(ec_slave_config_t *sc, uint16_t index,
+ uint8_t subindex, uint32_t value)
+{
+ uint8_t data[4];
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "subindex = 0x%02X, value = %u)\n",
+ __func__, sc, index, subindex, value);
+
+ EC_WRITE_U32(data, value);
+ return ecrt_slave_config_sdo(sc, index, subindex, data, 4);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_complete_sdo(ec_slave_config_t *sc, uint16_t index,
+ const uint8_t *data, size_t size)
+{
+ ec_slave_t *slave = sc->slave;
+ ec_sdo_request_t *req;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, index = 0x%04X, "
+ "data = 0x%p, size = %zu)\n", __func__, sc, index, data, size);
+
+ if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_COE)) {
+ EC_CONFIG_WARN(sc, "Attached slave does not support CoE!\n");
+ }
+
+ if (!(req = (ec_sdo_request_t *)
+ kmalloc(sizeof(ec_sdo_request_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate memory for"
+ " SDO configuration!\n");
+ return -ENOMEM;
+ }
+
+ ec_sdo_request_init(req);
+ ecrt_sdo_request_index(req, index, 0);
+ req->complete_access = 1;
+
+ ret = ec_sdo_request_copy_data(req, data, size);
+ if (ret < 0) {
+ ec_sdo_request_clear(req);
+ kfree(req);
+ return ret;
+ }
+
+ down(&sc->master->master_sem);
+ list_add_tail(&req->list, &sc->sdo_configs);
+ up(&sc->master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_emerg_size(ec_slave_config_t *sc, size_t elements)
+{
+ return ec_coe_emerg_ring_size(&sc->emerg_ring, elements);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_emerg_pop(ec_slave_config_t *sc, uint8_t *target)
+{
+ return ec_coe_emerg_ring_pop(&sc->emerg_ring, target);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_emerg_clear(ec_slave_config_t *sc)
+{
+ return ec_coe_emerg_ring_clear_ring(&sc->emerg_ring);
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_emerg_overruns(ec_slave_config_t *sc)
+{
+ return ec_coe_emerg_ring_overruns(&sc->emerg_ring);
+}
+
+/*****************************************************************************/
+
+/** Same as ecrt_slave_config_create_sdo_request(), but with ERR_PTR() return
+ * value.
+ */
+ec_sdo_request_t *ecrt_slave_config_create_sdo_request_err(
+ ec_slave_config_t *sc, uint16_t index, uint8_t subindex, size_t size)
+{
+ ec_sdo_request_t *req;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, "
+ "index = 0x%04X, subindex = 0x%02X, size = %zu)\n",
+ __func__, sc, index, subindex, size);
+
+ if (!(req = (ec_sdo_request_t *)
+ kmalloc(sizeof(ec_sdo_request_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate SDO request memory!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ec_sdo_request_init(req);
+ ecrt_sdo_request_index(req, index, subindex);
+
+ ret = ec_sdo_request_alloc(req, size);
+ if (ret < 0) {
+ ec_sdo_request_clear(req);
+ kfree(req);
+ return ERR_PTR(ret);
+ }
+
+ // prepare data for optional writing
+ memset(req->data, 0x00, size);
+ req->data_size = size;
+
+ down(&sc->master->master_sem);
+ list_add_tail(&req->list, &sc->sdo_requests);
+ up(&sc->master->master_sem);
+
+ return req;
+}
+
+/*****************************************************************************/
+
+ec_sdo_request_t *ecrt_slave_config_create_sdo_request(
+ ec_slave_config_t *sc, uint16_t index, uint8_t subindex, size_t size)
+{
+ ec_sdo_request_t *s = ecrt_slave_config_create_sdo_request_err(sc, index,
+ subindex, size);
+ return IS_ERR(s) ? NULL : s;
+}
+
+/*****************************************************************************/
+
+/** Same as ecrt_slave_config_create_reg_request(), but with ERR_PTR() return
+ * value.
+ */
+ec_reg_request_t *ecrt_slave_config_create_reg_request_err(
+ ec_slave_config_t *sc, size_t size)
+{
+ ec_reg_request_t *reg;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, size = %zu)\n",
+ __func__, sc, size);
+
+ if (!(reg = (ec_reg_request_t *)
+ kmalloc(sizeof(ec_reg_request_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate register request memory!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = ec_reg_request_init(reg, size);
+ if (ret) {
+ kfree(reg);
+ return ERR_PTR(ret);
+ }
+
+ down(&sc->master->master_sem);
+ list_add_tail(&reg->list, &sc->reg_requests);
+ up(&sc->master->master_sem);
+
+ return reg;
+}
+
+/*****************************************************************************/
+
+ec_reg_request_t *ecrt_slave_config_create_reg_request(
+ ec_slave_config_t *sc, size_t size)
+{
+ ec_reg_request_t *reg =
+ ecrt_slave_config_create_reg_request_err(sc, size);
+ return IS_ERR(reg) ? NULL : reg;
+}
+
+/*****************************************************************************/
+
+/** Same as ecrt_slave_config_create_voe_handler(), but with ERR_PTR() return
+ * value.
+ */
+ec_voe_handler_t *ecrt_slave_config_create_voe_handler_err(
+ ec_slave_config_t *sc, size_t size)
+{
+ ec_voe_handler_t *voe;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, size = %zu)\n", __func__, sc, size);
+
+ if (!(voe = (ec_voe_handler_t *)
+ kmalloc(sizeof(ec_voe_handler_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate VoE request memory!\n");
+ return ERR_PTR(-ENOMEM);
+ }
+
+ ret = ec_voe_handler_init(voe, sc, size);
+ if (ret < 0) {
+ kfree(voe);
+ return ERR_PTR(ret);
+ }
+
+ down(&sc->master->master_sem);
+ list_add_tail(&voe->list, &sc->voe_handlers);
+ up(&sc->master->master_sem);
+
+ return voe;
+}
+
+/*****************************************************************************/
+
+ec_voe_handler_t *ecrt_slave_config_create_voe_handler(
+ ec_slave_config_t *sc, size_t size)
+{
+ ec_voe_handler_t *voe = ecrt_slave_config_create_voe_handler_err(sc,
+ size);
+ return IS_ERR(voe) ? NULL : voe;
+}
+
+/*****************************************************************************/
+
+void ecrt_slave_config_state(const ec_slave_config_t *sc,
+ ec_slave_config_state_t *state)
+{
+ state->online = sc->slave ? 1 : 0;
+ if (state->online) {
+ state->operational =
+ sc->slave->current_state == EC_SLAVE_STATE_OP
+ && !sc->slave->force_config;
+ state->al_state = sc->slave->current_state;
+ } else {
+ state->operational = 0;
+ state->al_state = EC_SLAVE_STATE_UNKNOWN;
+ }
+}
+
+/*****************************************************************************/
+
+int ecrt_slave_config_idn(ec_slave_config_t *sc, uint8_t drive_no,
+ uint16_t idn, ec_al_state_t state, const uint8_t *data,
+ size_t size)
+{
+ ec_slave_t *slave = sc->slave;
+ ec_soe_request_t *req;
+ int ret;
+
+ EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, drive_no = %u, idn = 0x%04X, "
+ "state = %u, data = 0x%p, size = %zu)\n",
+ __func__, sc, drive_no, idn, state, data, size);
+
+ if (drive_no > 7) {
+ EC_CONFIG_ERR(sc, "Invalid drive number %u!\n",
+ (unsigned int) drive_no);
+ return -EINVAL;
+ }
+
+ if (state != EC_AL_STATE_PREOP && state != EC_AL_STATE_SAFEOP) {
+ EC_CONFIG_ERR(sc, "AL state for IDN config"
+ " must be PREOP or SAFEOP!\n");
+ return -EINVAL;
+ }
+
+ if (slave && !(slave->sii.mailbox_protocols & EC_MBOX_SOE)) {
+ EC_CONFIG_WARN(sc, "Attached slave does not support SoE!\n");
+ }
+
+ if (!(req = (ec_soe_request_t *)
+ kmalloc(sizeof(ec_soe_request_t), GFP_KERNEL))) {
+ EC_CONFIG_ERR(sc, "Failed to allocate memory for"
+ " IDN configuration!\n");
+ return -ENOMEM;
+ }
+
+ ec_soe_request_init(req);
+ ec_soe_request_set_drive_no(req, drive_no);
+ ec_soe_request_set_idn(req, idn);
+ req->al_state = state;
+
+ ret = ec_soe_request_copy_data(req, data, size);
+ if (ret < 0) {
+ ec_soe_request_clear(req);
+ kfree(req);
+ return ret;
+ }
+
+ down(&sc->master->master_sem);
+ list_add_tail(&req->list, &sc->soe_configs);
+ up(&sc->master->master_sem);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_slave_config_sync_manager);
+EXPORT_SYMBOL(ecrt_slave_config_watchdog);
+EXPORT_SYMBOL(ecrt_slave_config_pdo_assign_add);
+EXPORT_SYMBOL(ecrt_slave_config_pdo_assign_clear);
+EXPORT_SYMBOL(ecrt_slave_config_pdo_mapping_add);
+EXPORT_SYMBOL(ecrt_slave_config_pdo_mapping_clear);
+EXPORT_SYMBOL(ecrt_slave_config_pdos);
+EXPORT_SYMBOL(ecrt_slave_config_reg_pdo_entry);
+EXPORT_SYMBOL(ecrt_slave_config_dc);
+EXPORT_SYMBOL(ecrt_slave_config_sdo);
+EXPORT_SYMBOL(ecrt_slave_config_sdo8);
+EXPORT_SYMBOL(ecrt_slave_config_sdo16);
+EXPORT_SYMBOL(ecrt_slave_config_sdo32);
+EXPORT_SYMBOL(ecrt_slave_config_complete_sdo);
+EXPORT_SYMBOL(ecrt_slave_config_emerg_size);
+EXPORT_SYMBOL(ecrt_slave_config_emerg_pop);
+EXPORT_SYMBOL(ecrt_slave_config_emerg_clear);
+EXPORT_SYMBOL(ecrt_slave_config_emerg_overruns);
+EXPORT_SYMBOL(ecrt_slave_config_create_sdo_request);
+EXPORT_SYMBOL(ecrt_slave_config_create_voe_handler);
+EXPORT_SYMBOL(ecrt_slave_config_create_reg_request);
+EXPORT_SYMBOL(ecrt_slave_config_state);
+EXPORT_SYMBOL(ecrt_slave_config_idn);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/slave_config.h b/net/ethercat/master/slave_config.h
new file mode 100644
index 000000000000..9fd633dcb2b9
--- /dev/null
+++ b/net/ethercat/master/slave_config.h
@@ -0,0 +1,185 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2012 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 slave configuration structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SLAVE_CONFIG_H__
+#define __EC_SLAVE_CONFIG_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "slave.h"
+#include "sync_config.h"
+#include "fmmu_config.h"
+#include "coe_emerg_ring.h"
+
+/*****************************************************************************/
+
+/** Convenience macro for printing configuration-specific information to
+ * syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX> <ALIAS>:<POSITION>: ", where INDEX is the master index
+ * and ALIAS and POSITION identify the configuration.
+ *
+ * \param sc EtherCAT slave configuration
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_CONFIG_INFO(sc, fmt, args...) \
+ printk(KERN_INFO "EtherCAT %u %u:%u: " fmt, sc->master->index, \
+ sc->alias, sc->position, ##args)
+
+/** Convenience macro for printing configuration-specific errors to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX> <ALIAS>:<POSITION>: ", where INDEX is the master index
+ * and ALIAS and POSITION identify the configuration.
+ *
+ * \param sc EtherCAT slave configuration
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_CONFIG_ERR(sc, fmt, args...) \
+ printk(KERN_ERR "EtherCAT ERROR %u %u:%u: " fmt, sc->master->index, \
+ sc->alias, sc->position, ##args)
+
+/** Convenience macro for printing configuration-specific warnings to syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX> <ALIAS>:<POSITION>: ", where INDEX is the master index
+ * and ALIAS and POSITION identify the configuration.
+ *
+ * \param sc EtherCAT slave configuration
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_CONFIG_WARN(sc, fmt, args...) \
+ printk(KERN_WARNING "EtherCAT WARNING %u %u:%u: " fmt, \
+ sc->master->index, sc->alias, sc->position, ##args)
+
+/** Convenience macro for printing configuration-specific debug messages to
+ * syslog.
+ *
+ * This will print the message in \a fmt with a prefixed
+ * "EtherCAT <INDEX> <ALIAS>:<POSITION>: ", where INDEX is the master index
+ * and ALIAS and POSITION identify the configuration.
+ *
+ * \param sc EtherCAT slave configuration
+ * \param level Debug level. Master's debug level must be >= \a level for
+ * output.
+ * \param fmt format string (like in printf())
+ * \param args arguments (optional)
+ */
+#define EC_CONFIG_DBG(sc, level, fmt, args...) \
+ do { \
+ if (sc->master->debug_level >= level) { \
+ printk(KERN_DEBUG "EtherCAT DEBUG %u %u:%u: " fmt, \
+ sc->master->index, sc->alias, sc->position, ##args); \
+ } \
+ } while (0)
+
+/*****************************************************************************/
+
+/** EtherCAT slave configuration.
+ */
+struct ec_slave_config {
+ struct list_head list; /**< List item. */
+ ec_master_t *master; /**< Master owning the slave configuration. */
+
+ uint16_t alias; /**< Slave alias. */
+ uint16_t position; /**< Index after alias. If alias is zero, this is the
+ ring position. */
+ uint32_t vendor_id; /**< Slave vendor ID. */
+ uint32_t product_code; /**< Slave product code. */
+
+ uint16_t watchdog_divider; /**< Watchdog divider as a number of 40ns
+ intervals (see spec. reg. 0x0400). */
+ uint16_t watchdog_intervals; /**< Process data watchdog intervals (see
+ spec. reg. 0x0420). */
+
+ ec_slave_t *slave; /**< Slave pointer. This is \a NULL, if the slave is
+ offline. */
+
+ ec_sync_config_t sync_configs[EC_MAX_SYNC_MANAGERS]; /**< Sync manager
+ configurations. */
+ ec_fmmu_config_t fmmu_configs[EC_MAX_FMMUS]; /**< FMMU configurations. */
+ uint8_t used_fmmus; /**< Number of FMMUs used. */
+ uint16_t dc_assign_activate; /**< Vendor-specific AssignActivate word. */
+ ec_sync_signal_t dc_sync[EC_SYNC_SIGNAL_COUNT]; /**< DC sync signals. */
+
+ struct list_head sdo_configs; /**< List of SDO configurations. */
+ struct list_head sdo_requests; /**< List of SDO requests. */
+ struct list_head voe_handlers; /**< List of VoE handlers. */
+ struct list_head reg_requests; /**< List of register requests. */
+ struct list_head soe_configs; /**< List of SoE configurations. */
+
+ ec_coe_emerg_ring_t emerg_ring; /**< CoE emergency ring buffer. */
+};
+
+/*****************************************************************************/
+
+void ec_slave_config_init(ec_slave_config_t *, ec_master_t *, uint16_t,
+ uint16_t, uint32_t, uint32_t);
+void ec_slave_config_clear(ec_slave_config_t *);
+
+int ec_slave_config_attach(ec_slave_config_t *);
+void ec_slave_config_detach(ec_slave_config_t *);
+
+void ec_slave_config_load_default_sync_config(ec_slave_config_t *);
+
+unsigned int ec_slave_config_sdo_count(const ec_slave_config_t *);
+const ec_sdo_request_t *ec_slave_config_get_sdo_by_pos_const(
+ const ec_slave_config_t *, unsigned int);
+unsigned int ec_slave_config_idn_count(const ec_slave_config_t *);
+const ec_soe_request_t *ec_slave_config_get_idn_by_pos_const(
+ const ec_slave_config_t *, unsigned int);
+ec_sdo_request_t *ec_slave_config_find_sdo_request(ec_slave_config_t *,
+ unsigned int);
+ec_reg_request_t *ec_slave_config_find_reg_request(ec_slave_config_t *,
+ unsigned int);
+ec_voe_handler_t *ec_slave_config_find_voe_handler(ec_slave_config_t *,
+ unsigned int);
+
+ec_sdo_request_t *ecrt_slave_config_create_sdo_request_err(
+ ec_slave_config_t *, uint16_t, uint8_t, size_t);
+ec_voe_handler_t *ecrt_slave_config_create_voe_handler_err(
+ ec_slave_config_t *, size_t);
+ec_reg_request_t *ecrt_slave_config_create_reg_request_err(
+ ec_slave_config_t *, size_t);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/soe_errors.c b/net/ethercat/master/soe_errors.c
new file mode 100644
index 000000000000..b7d1c5dc619a
--- /dev/null
+++ b/net/ethercat/master/soe_errors.c
@@ -0,0 +1,96 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 SoE errors.
+*/
+
+/*****************************************************************************/
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** SoE error codes.
+ */
+const ec_code_msg_t soe_error_codes[] = {
+ {0x1001, "No IDN"},
+ {0x1009, "Invalid access to element 1"},
+ {0x2001, "No name"},
+ {0x2002, "Name transmission too short"},
+ {0x2003, "Name transmission too long"},
+ {0x2004, "Name cannot be changed, read only"},
+ {0x2005, "Name is write protected at this time"},
+ {0x3002, "Attribute transmission too short"},
+ {0x3003, "Attribute transmission too long"},
+ {0x3004, "Attribute cannot be changed, read only"},
+ {0x3005, "Attribute is write protected at this time"},
+ {0x4001, "No unit"},
+ {0x4002, "Unit transmission too short"},
+ {0x4003, "Unit transmission too long"},
+ {0x4004, "Unit cannot be changed, read only"},
+ {0x4005, "Unit is write proteced at this time"},
+ {0x5001, "No minimum input value"},
+ {0x5002, "Minimum input value transmission too short"},
+ {0x5003, "Minimum input value transmission too long"},
+ {0x5004, "Minimum input value cannot be changed, read only"},
+ {0x5005, "Minimum input value is write protected at this time"},
+ {0x6001, "No maximum input value"},
+ {0x6002, "Maximum input value transmission too short"},
+ {0x6003, "Maximum input value transmission too long"},
+ {0x6004, "Maximum input value cannot be changed, read only"},
+ {0x6005, "Maximum input value is write protected at this time"},
+ {0x7002, "Operation data value transmission too short"},
+ {0x7003, "Operation data value transmission too long"},
+ {0x7004, "Operation data value cannot be changed, read only"},
+ {0x7005, "Operation data value is write protected at this time"},
+ {0x7006, "Operation data value is smaller than the minimum input value"},
+ {0x7007, "Operation data value is greater than the minimum input value"},
+ {0x7008, "Invalid operation data"},
+ {0x7009, "Operation data is write protected by a password"},
+ {0x700A, "Operation data is write protected"},
+ {0x700B, "Invalid indirect addressing"},
+ {0x700C, "Operation data is write protected due to other settings"},
+ {0x700D, "Reserved"},
+ {0x7010, "Procedure command already active"},
+ {0x7011, "Procedure command not interruptible"},
+ {0x7012, "Procedure command is at this time not executable"},
+ {0x7013, "Procedure command not executable"},
+ {0x7014, "No data state"},
+ {0x8001, "No default value"},
+ {0x8002, "Default value transmission too long"},
+ {0x8004, "Default value cannot be changed, read only"},
+ {0x800A, "Invalid drive number"},
+ {0x800B, "General error"},
+ {0x800C, "No element addressed"},
+ {}
+};
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/soe_request.c b/net/ethercat/master/soe_request.c
new file mode 100644
index 000000000000..4ad903533d76
--- /dev/null
+++ b/net/ethercat/master/soe_request.c
@@ -0,0 +1,254 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ * Sercos-over-EtherCAT request functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "soe_request.h"
+
+/*****************************************************************************/
+
+/** Default timeout in ms to wait for SoE responses.
+ */
+#define EC_SOE_REQUEST_RESPONSE_TIMEOUT 1000
+
+/*****************************************************************************/
+
+void ec_soe_request_clear_data(ec_soe_request_t *);
+
+/*****************************************************************************/
+
+/** SoE request constructor.
+ */
+void ec_soe_request_init(
+ ec_soe_request_t *req /**< SoE request. */
+ )
+{
+ INIT_LIST_HEAD(&req->list);
+ req->drive_no = 0x00;
+ req->idn = 0x0000;
+ req->al_state = EC_AL_STATE_INIT;
+ req->data = NULL;
+ req->mem_size = 0;
+ req->data_size = 0;
+ req->dir = EC_DIR_INVALID;
+ req->state = EC_INT_REQUEST_INIT;
+ req->jiffies_sent = 0U;
+ req->error_code = 0x0000;
+}
+
+/*****************************************************************************/
+
+/** SoE request destructor.
+ */
+void ec_soe_request_clear(
+ ec_soe_request_t *req /**< SoE request. */
+ )
+{
+ ec_soe_request_clear_data(req);
+}
+
+/*****************************************************************************/
+
+/** Copy another SoE request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+int ec_soe_request_copy(
+ ec_soe_request_t *req, /**< SoE request. */
+ const ec_soe_request_t *other /**< Other SoE request to copy from. */
+ )
+{
+ req->drive_no = other->drive_no;
+ req->idn = other->idn;
+ req->al_state = other->al_state;
+ return ec_soe_request_copy_data(req, other->data, other->data_size);
+}
+
+/*****************************************************************************/
+
+/** Set drive number.
+ */
+void ec_soe_request_set_drive_no(
+ ec_soe_request_t *req, /**< SoE request. */
+ uint8_t drive_no /** Drive Number. */
+ )
+{
+ req->drive_no = drive_no;
+}
+
+/*****************************************************************************/
+
+/** Set IDN.
+ */
+void ec_soe_request_set_idn(
+ ec_soe_request_t *req, /**< SoE request. */
+ uint16_t idn /** IDN. */
+ )
+{
+ req->idn = idn;
+}
+
+/*****************************************************************************/
+
+/** Free allocated memory.
+ */
+void ec_soe_request_clear_data(
+ ec_soe_request_t *req /**< SoE request. */
+ )
+{
+ if (req->data) {
+ kfree(req->data);
+ req->data = NULL;
+ }
+
+ req->mem_size = 0;
+ req->data_size = 0;
+}
+
+/*****************************************************************************/
+
+/** Pre-allocates the data memory.
+ *
+ * If the \a mem_size is already bigger than \a size, nothing is done.
+ *
+ * \return 0 on success, otherwise -ENOMEM.
+ */
+int ec_soe_request_alloc(
+ ec_soe_request_t *req, /**< SoE request. */
+ size_t size /**< Data size to allocate. */
+ )
+{
+ if (size <= req->mem_size)
+ return 0;
+
+ ec_soe_request_clear_data(req);
+
+ if (!(req->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
+ EC_ERR("Failed to allocate %zu bytes of SoE memory.\n", size);
+ return -ENOMEM;
+ }
+
+ req->mem_size = size;
+ req->data_size = 0;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Copies SoE data from an external source.
+ *
+ * If the \a mem_size is to small, new memory is allocated.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_soe_request_copy_data(
+ ec_soe_request_t *req, /**< SoE request. */
+ const uint8_t *source, /**< Source data. */
+ size_t size /**< Number of bytes in \a source. */
+ )
+{
+ int ret = ec_soe_request_alloc(req, size);
+ if (ret < 0)
+ return ret;
+
+ memcpy(req->data, source, size);
+ req->data_size = size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Copies SoE data from an external source.
+ *
+ * If the \a mem_size is to small, new memory is allocated.
+ *
+ * \retval 0 Success.
+ * \retval <0 Error code.
+ */
+int ec_soe_request_append_data(
+ ec_soe_request_t *req, /**< SoE request. */
+ const uint8_t *source, /**< Source data. */
+ size_t size /**< Number of bytes in \a source. */
+ )
+{
+ if (req->data_size + size > req->mem_size) {
+ size_t new_size = req->mem_size ? req->mem_size * 2 : size;
+ uint8_t *new_data = (uint8_t *) kmalloc(new_size, GFP_KERNEL);
+ if (!new_data) {
+ EC_ERR("Failed to allocate %zu bytes of SoE memory.\n",
+ new_size);
+ return -ENOMEM;
+ }
+ memcpy(new_data, req->data, req->data_size);
+ kfree(req->data);
+ req->data = new_data;
+ req->mem_size = new_size;
+ }
+
+ memcpy(req->data + req->data_size, source, size);
+ req->data_size += size;
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Request a read operation.
+ */
+void ec_soe_request_read(
+ ec_soe_request_t *req /**< SoE request. */
+ )
+{
+ req->dir = EC_DIR_INPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->error_code = 0x0000;
+}
+
+/*****************************************************************************/
+
+/** Request a write operation.
+ */
+void ec_soe_request_write(
+ ec_soe_request_t *req /**< SoE request. */
+ )
+{
+ req->dir = EC_DIR_OUTPUT;
+ req->state = EC_INT_REQUEST_QUEUED;
+ req->error_code = 0x0000;
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/soe_request.h b/net/ethercat/master/soe_request.h
new file mode 100644
index 000000000000..40f61aa677a8
--- /dev/null
+++ b/net/ethercat/master/soe_request.h
@@ -0,0 +1,80 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 SoE request structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_SOE_REQUEST_H__
+#define __EC_SOE_REQUEST_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** Sercos-over-EtherCAT request.
+ */
+typedef struct {
+ struct list_head list; /**< List item. */
+ uint8_t drive_no; /**< Drive number. */
+ uint16_t idn; /**< Sercos ID-Number. */
+ ec_al_state_t al_state; /**< AL state (only valid for IDN config). */
+ uint8_t *data; /**< Pointer to SDO data. */
+ size_t mem_size; /**< Size of SDO data memory. */
+ size_t data_size; /**< Size of SDO data. */
+ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means writing to the
+ slave, EC_DIR_INPUT means reading from the slave. */
+ ec_internal_request_state_t state; /**< Request state. */
+ unsigned long jiffies_sent; /**< Jiffies, when the upload/download
+ request was sent. */
+ uint16_t error_code; /**< SoE error code. */
+} ec_soe_request_t;
+
+/*****************************************************************************/
+
+void ec_soe_request_init(ec_soe_request_t *);
+void ec_soe_request_clear(ec_soe_request_t *);
+
+int ec_soe_request_copy(ec_soe_request_t *, const ec_soe_request_t *);
+void ec_soe_request_set_drive_no(ec_soe_request_t *, uint8_t);
+void ec_soe_request_set_idn(ec_soe_request_t *, uint16_t);
+int ec_soe_request_alloc(ec_soe_request_t *, size_t);
+int ec_soe_request_copy_data(ec_soe_request_t *, const uint8_t *, size_t);
+int ec_soe_request_append_data(ec_soe_request_t *, const uint8_t *, size_t);
+void ec_soe_request_read(ec_soe_request_t *);
+void ec_soe_request_write(ec_soe_request_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/sync.c b/net/ethercat/master/sync.c
new file mode 100644
index 000000000000..0002e8734044
--- /dev/null
+++ b/net/ethercat/master/sync.c
@@ -0,0 +1,178 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 sync manager methods.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "slave.h"
+#include "master.h"
+#include "pdo.h"
+#include "sync.h"
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_sync_init(
+ ec_sync_t *sync, /**< EtherCAT sync manager. */
+ ec_slave_t *slave /**< EtherCAT slave. */
+ )
+{
+ sync->slave = slave;
+ sync->physical_start_address = 0x0000;
+ sync->default_length = 0x0000;
+ sync->control_register = 0x00;
+ sync->enable = 0x00;
+ ec_pdo_list_init(&sync->pdos);
+}
+
+/*****************************************************************************/
+
+/** Copy constructor.
+ */
+void ec_sync_init_copy(
+ ec_sync_t *sync, /**< EtherCAT sync manager. */
+ const ec_sync_t *other /**< Sync manager to copy from. */
+ )
+{
+ sync->slave = other->slave;
+ sync->physical_start_address = other->physical_start_address;
+ sync->default_length = other->default_length;
+ sync->control_register = other->control_register;
+ sync->enable = other->enable;
+ ec_pdo_list_init(&sync->pdos);
+ ec_pdo_list_copy(&sync->pdos, &other->pdos);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_sync_clear(
+ ec_sync_t *sync /**< EtherCAT sync manager. */
+ )
+{
+ ec_pdo_list_clear(&sync->pdos);
+}
+
+/*****************************************************************************/
+
+/** Initializes a sync manager configuration page.
+ *
+ * The referenced memory (\a data) must be at least \a EC_SYNC_SIZE bytes.
+ */
+void ec_sync_page(
+ const ec_sync_t *sync, /**< Sync manager. */
+ uint8_t sync_index, /**< Index of the sync manager. */
+ uint16_t data_size, /**< Data size. */
+ const ec_sync_config_t *sync_config, /**< Configuration. */
+ uint8_t pdo_xfer, /**< Non-zero, if PDOs will be transferred via this
+ sync manager. */
+ uint8_t *data /**> Configuration memory. */
+ )
+{
+ // enable only if (SII enable is set or PDO xfer)
+ // and size is > 0 and SM is not virtual
+ uint16_t enable = ((sync->enable & 0x01) || pdo_xfer)
+ && data_size
+ && ((sync->enable & 0x04) == 0);
+ uint8_t control = sync->control_register;
+
+ if (sync_config) {
+
+ switch (sync_config->dir) {
+ case EC_DIR_OUTPUT:
+ case EC_DIR_INPUT:
+ EC_WRITE_BIT(&control, 2,
+ sync_config->dir == EC_DIR_OUTPUT ? 1 : 0);
+ EC_WRITE_BIT(&control, 3, 0);
+ break;
+ default:
+ break;
+ }
+
+ switch (sync_config->watchdog_mode) {
+ case EC_WD_ENABLE:
+ case EC_WD_DISABLE:
+ EC_WRITE_BIT(&control, 6,
+ sync_config->watchdog_mode == EC_WD_ENABLE);
+ break;
+ default:
+ break;
+ }
+ }
+
+ EC_SLAVE_DBG(sync->slave, 1, "SM%u: Addr 0x%04X, Size %3u,"
+ " Ctrl 0x%02X, En %u\n",
+ sync_index, sync->physical_start_address,
+ data_size, control, enable);
+
+ EC_WRITE_U16(data, sync->physical_start_address);
+ EC_WRITE_U16(data + 2, data_size);
+ EC_WRITE_U8 (data + 4, control);
+ EC_WRITE_U8 (data + 5, 0x00); // status byte (read only)
+ EC_WRITE_U16(data + 6, enable);
+}
+
+/*****************************************************************************/
+
+/** Adds a PDO to the list of known mapped PDOs.
+ *
+ * \return 0 on success, else < 0
+ */
+int ec_sync_add_pdo(
+ ec_sync_t *sync, /**< EtherCAT sync manager. */
+ const ec_pdo_t *pdo /**< PDO to map. */
+ )
+{
+ return ec_pdo_list_add_pdo_copy(&sync->pdos, pdo);
+}
+
+/*****************************************************************************/
+
+/** Determines the default direction from the control register.
+ *
+ * \return Direction.
+ */
+ec_direction_t ec_sync_default_direction(
+ const ec_sync_t *sync /**< EtherCAT sync manager. */
+ )
+{
+ switch ((sync->control_register & 0x0C) >> 2) {
+ case 0x0: return EC_DIR_INPUT;
+ case 0x1: return EC_DIR_OUTPUT;
+ default: return EC_DIR_INVALID;
+ }
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/sync.h b/net/ethercat/master/sync.h
new file mode 100644
index 000000000000..30f8f3481a58
--- /dev/null
+++ b/net/ethercat/master/sync.h
@@ -0,0 +1,68 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 sync manager.
+ */
+
+/*****************************************************************************/
+
+#ifndef __EC_SYNC_H__
+#define __EC_SYNC_H__
+
+#include "globals.h"
+#include "pdo_list.h"
+#include "sync_config.h"
+
+/*****************************************************************************/
+
+/** Sync manager.
+ */
+typedef struct {
+ ec_slave_t *slave; /**< Slave, the sync manager belongs to. */
+ uint16_t physical_start_address; /**< Physical start address. */
+ uint16_t default_length; /**< Data length in bytes. */
+ uint8_t control_register; /**< Control register value. */
+ uint8_t enable; /**< Enable bit. */
+ ec_pdo_list_t pdos; /**< Current PDO assignment. */
+} ec_sync_t;
+
+/*****************************************************************************/
+
+void ec_sync_init(ec_sync_t *, ec_slave_t *);
+void ec_sync_init_copy(ec_sync_t *, const ec_sync_t *);
+void ec_sync_clear(ec_sync_t *);
+void ec_sync_page(const ec_sync_t *, uint8_t, uint16_t,
+ const ec_sync_config_t *, uint8_t, uint8_t *);
+int ec_sync_add_pdo(ec_sync_t *, const ec_pdo_t *);
+ec_direction_t ec_sync_default_direction(const ec_sync_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/sync_config.c b/net/ethercat/master/sync_config.c
new file mode 100644
index 000000000000..e3c441d21326
--- /dev/null
+++ b/net/ethercat/master/sync_config.c
@@ -0,0 +1,63 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 sync manager configuration methods.
+ */
+
+/*****************************************************************************/
+
+#include "globals.h"
+#include "sync_config.h"
+
+/*****************************************************************************/
+
+/** Constructor.
+ */
+void ec_sync_config_init(
+ ec_sync_config_t *sync_config /**< Sync manager configuration. */
+ )
+{
+ sync_config->dir = EC_DIR_INVALID;
+ sync_config->watchdog_mode = EC_WD_DEFAULT;
+ ec_pdo_list_init(&sync_config->pdos);
+}
+
+/*****************************************************************************/
+
+/** Destructor.
+ */
+void ec_sync_config_clear(
+ ec_sync_config_t *sync_config /**< Sync manager configuration. */
+ )
+{
+ ec_pdo_list_clear(&sync_config->pdos);
+}
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/sync_config.h b/net/ethercat/master/sync_config.h
new file mode 100644
index 000000000000..c8fc3e88fe41
--- /dev/null
+++ b/net/ethercat/master/sync_config.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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 sync manager.
+ */
+
+/*****************************************************************************/
+
+#ifndef __EC_SYNC_CONFIG_H__
+#define __EC_SYNC_CONFIG_H__
+
+#include "globals.h"
+#include "pdo_list.h"
+
+/*****************************************************************************/
+
+/** Sync manager configuration.
+ */
+typedef struct {
+ ec_direction_t dir; /**< Sync manager direction. */
+ ec_watchdog_mode_t watchdog_mode; /**< Watchdog mode. */
+ ec_pdo_list_t pdos; /**< Current PDO assignment. */
+} ec_sync_config_t;
+
+/*****************************************************************************/
+
+void ec_sync_config_init(ec_sync_config_t *);
+void ec_sync_config_clear(ec_sync_config_t *);
+
+/*****************************************************************************/
+
+#endif
diff --git a/net/ethercat/master/voe_handler.c b/net/ethercat/master/voe_handler.c
new file mode 100644
index 000000000000..6ad1fc416bf5
--- /dev/null
+++ b/net/ethercat/master/voe_handler.c
@@ -0,0 +1,560 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ * Vendor specific over EtherCAT protocol handler functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+
+#include "master.h"
+#include "slave_config.h"
+#include "mailbox.h"
+#include "voe_handler.h"
+
+/** VoE mailbox type.
+ */
+#define EC_MBOX_TYPE_VOE 0x0f
+
+/** VoE header size.
+ */
+#define EC_VOE_HEADER_SIZE 6
+
+/** VoE response timeout in [ms].
+ */
+#define EC_VOE_RESPONSE_TIMEOUT 500
+
+/*****************************************************************************/
+
+void ec_voe_handler_state_write_start(ec_voe_handler_t *);
+void ec_voe_handler_state_write_response(ec_voe_handler_t *);
+
+void ec_voe_handler_state_read_start(ec_voe_handler_t *);
+void ec_voe_handler_state_read_check(ec_voe_handler_t *);
+void ec_voe_handler_state_read_response(ec_voe_handler_t *);
+
+void ec_voe_handler_state_read_nosync_start(ec_voe_handler_t *);
+void ec_voe_handler_state_read_nosync_response(ec_voe_handler_t *);
+
+void ec_voe_handler_state_end(ec_voe_handler_t *);
+void ec_voe_handler_state_error(ec_voe_handler_t *);
+
+/*****************************************************************************/
+
+/** VoE handler constructor.
+ *
+ * \return Return value of ec_datagram_prealloc().
+ */
+int ec_voe_handler_init(
+ ec_voe_handler_t *voe, /**< VoE handler. */
+ ec_slave_config_t *sc, /**< Parent slave configuration. */
+ size_t size /**< Size of memory to reserve. */
+ )
+{
+ voe->config = sc;
+ voe->vendor_id = 0x00000000;
+ voe->vendor_type = 0x0000;
+ voe->data_size = 0;
+ voe->dir = EC_DIR_INVALID;
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_INIT;
+
+ ec_datagram_init(&voe->datagram);
+ return ec_datagram_prealloc(&voe->datagram,
+ size + EC_MBOX_HEADER_SIZE + EC_VOE_HEADER_SIZE);
+}
+
+/*****************************************************************************/
+
+/** VoE handler destructor.
+ */
+void ec_voe_handler_clear(
+ ec_voe_handler_t *voe /**< VoE handler. */
+ )
+{
+ ec_datagram_clear(&voe->datagram);
+}
+
+/*****************************************************************************/
+
+/** Get usable memory size.
+ *
+ * \return Memory size.
+ */
+size_t ec_voe_handler_mem_size(
+ const ec_voe_handler_t *voe /**< VoE handler. */
+ )
+{
+ if (voe->datagram.mem_size >= EC_MBOX_HEADER_SIZE + EC_VOE_HEADER_SIZE)
+ return voe->datagram.mem_size -
+ (EC_MBOX_HEADER_SIZE + EC_VOE_HEADER_SIZE);
+ else
+ return 0;
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+void ecrt_voe_handler_send_header(ec_voe_handler_t *voe, uint32_t vendor_id,
+ uint16_t vendor_type)
+{
+ voe->vendor_id = vendor_id;
+ voe->vendor_type = vendor_type;
+}
+
+/*****************************************************************************/
+
+void ecrt_voe_handler_received_header(const ec_voe_handler_t *voe,
+ uint32_t *vendor_id, uint16_t *vendor_type)
+{
+ uint8_t *header = voe->datagram.data + EC_MBOX_HEADER_SIZE;
+
+ if (vendor_id)
+ *vendor_id = EC_READ_U32(header);
+ if (vendor_type)
+ *vendor_type = EC_READ_U16(header + 4);
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_voe_handler_data(ec_voe_handler_t *voe)
+{
+ return voe->datagram.data + EC_MBOX_HEADER_SIZE + EC_VOE_HEADER_SIZE;
+}
+
+/*****************************************************************************/
+
+size_t ecrt_voe_handler_data_size(const ec_voe_handler_t *voe)
+{
+ return voe->data_size;
+}
+
+/*****************************************************************************/
+
+void ecrt_voe_handler_read(ec_voe_handler_t *voe)
+{
+ voe->dir = EC_DIR_INPUT;
+ voe->state = ec_voe_handler_state_read_start;
+ voe->request_state = EC_INT_REQUEST_BUSY;
+}
+
+/*****************************************************************************/
+
+void ecrt_voe_handler_read_nosync(ec_voe_handler_t *voe)
+{
+ voe->dir = EC_DIR_INPUT;
+ voe->state = ec_voe_handler_state_read_nosync_start;
+ voe->request_state = EC_INT_REQUEST_BUSY;
+}
+
+/*****************************************************************************/
+
+void ecrt_voe_handler_write(ec_voe_handler_t *voe, size_t size)
+{
+ voe->dir = EC_DIR_OUTPUT;
+ voe->data_size = size;
+ voe->state = ec_voe_handler_state_write_start;
+ voe->request_state = EC_INT_REQUEST_BUSY;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_voe_handler_execute(ec_voe_handler_t *voe)
+{
+ if (voe->config->slave) { // FIXME locking?
+ voe->state(voe);
+ if (voe->request_state == EC_INT_REQUEST_BUSY) {
+ ec_master_queue_datagram(voe->config->master, &voe->datagram);
+ }
+ } else {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ }
+
+ return ec_request_state_translation_table[voe->request_state];
+}
+
+/******************************************************************************
+ * State functions.
+ *****************************************************************************/
+
+/** Start writing VoE data.
+ */
+void ec_voe_handler_state_write_start(ec_voe_handler_t *voe)
+{
+ ec_slave_t *slave = voe->config->slave;
+ uint8_t *data;
+
+ if (slave->master->debug_level) {
+ EC_SLAVE_DBG(slave, 0, "Writing %zu bytes of VoE data.\n",
+ voe->data_size);
+ ec_print_data(ecrt_voe_handler_data(voe), voe->data_size);
+ }
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_VOE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support VoE!\n");
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ data = ec_slave_mbox_prepare_send(slave, &voe->datagram,
+ EC_MBOX_TYPE_VOE, EC_VOE_HEADER_SIZE + voe->data_size);
+ if (IS_ERR(data)) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ EC_WRITE_U32(data, voe->vendor_id);
+ EC_WRITE_U16(data + 4, voe->vendor_type);
+ /* data already in datagram */
+
+ voe->retries = EC_FSM_RETRIES;
+ voe->jiffies_start = jiffies;
+ voe->state = ec_voe_handler_state_write_response;
+}
+
+/*****************************************************************************/
+
+/** Wait for the mailbox response.
+ */
+void ec_voe_handler_state_write_response(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && voe->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Failed to receive VoE write request datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ if (!datagram->working_counter) {
+ unsigned long diff_ms =
+ (jiffies - voe->jiffies_start) * 1000 / HZ;
+ if (diff_ms < EC_VOE_RESPONSE_TIMEOUT) {
+ EC_SLAVE_DBG(slave, 1, "Slave did not respond to"
+ " VoE write request. Retrying after %lu ms...\n",
+ diff_ms);
+ // no response; send request datagram again
+ return;
+ }
+ }
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Reception of VoE write request failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ EC_CONFIG_DBG(voe->config, 1, "VoE write request successful.\n");
+
+ voe->request_state = EC_INT_REQUEST_SUCCESS;
+ voe->state = ec_voe_handler_state_end;
+}
+
+/*****************************************************************************/
+
+/** Start reading VoE data.
+ */
+void ec_voe_handler_state_read_start(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+
+ EC_SLAVE_DBG(slave, 1, "Reading VoE data.\n");
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_VOE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support VoE!\n");
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+
+ voe->jiffies_start = jiffies;
+ voe->retries = EC_FSM_RETRIES;
+ voe->state = ec_voe_handler_state_read_check;
+}
+
+/*****************************************************************************/
+
+/** Check for new data in the mailbox.
+ */
+void ec_voe_handler_state_read_check(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && voe->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Failed to receive VoE mailbox check datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Reception of VoE mailbox check"
+ " datagram failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ if (!ec_slave_mbox_check(datagram)) {
+ unsigned long diff_ms =
+ (datagram->jiffies_received - voe->jiffies_start) * 1000 / HZ;
+ if (diff_ms >= EC_VOE_RESPONSE_TIMEOUT) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Timeout while waiting for VoE data.\n");
+ return;
+ }
+
+ ec_slave_mbox_prepare_check(slave, datagram); // can not fail.
+ voe->retries = EC_FSM_RETRIES;
+ return;
+ }
+
+ // Fetch response
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+ voe->retries = EC_FSM_RETRIES;
+ voe->state = ec_voe_handler_state_read_response;
+}
+
+/*****************************************************************************/
+
+/** Read the pending mailbox data.
+ */
+void ec_voe_handler_state_read_response(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+ ec_master_t *master = voe->config->master;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && voe->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Failed to receive VoE read datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Reception of VoE read response failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ data = ec_slave_mbox_fetch(slave, datagram, &mbox_prot, &rec_size);
+ if (IS_ERR(data)) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_VOE) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_WARN(slave, "Received mailbox protocol 0x%02X"
+ " as response.\n", mbox_prot);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (rec_size < EC_VOE_HEADER_SIZE) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Received VoE header is"
+ " incomplete (%zu bytes)!\n", rec_size);
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_CONFIG_DBG(voe->config, 0, "VoE data:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ voe->data_size = rec_size - EC_VOE_HEADER_SIZE;
+ voe->request_state = EC_INT_REQUEST_SUCCESS;
+ voe->state = ec_voe_handler_state_end; // success
+}
+
+/*****************************************************************************/
+
+/** Start reading VoE data without sending a sync message before.
+ */
+void ec_voe_handler_state_read_nosync_start(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+
+ EC_SLAVE_DBG(slave, 1, "Reading VoE data.\n");
+
+ if (!(slave->sii.mailbox_protocols & EC_MBOX_VOE)) {
+ EC_SLAVE_ERR(slave, "Slave does not support VoE!\n");
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ ec_slave_mbox_prepare_fetch(slave, datagram); // can not fail.
+
+ voe->jiffies_start = jiffies;
+ voe->retries = EC_FSM_RETRIES;
+ voe->state = ec_voe_handler_state_read_nosync_response;
+}
+
+/*****************************************************************************/
+
+/** Read the pending mailbox data without sending a sync message before. This
+ * might lead to an empty reponse from the client.
+ */
+void ec_voe_handler_state_read_nosync_response(ec_voe_handler_t *voe)
+{
+ ec_datagram_t *datagram = &voe->datagram;
+ ec_slave_t *slave = voe->config->slave;
+ ec_master_t *master = voe->config->master;
+ uint8_t *data, mbox_prot;
+ size_t rec_size;
+
+ if (datagram->state == EC_DATAGRAM_TIMED_OUT && voe->retries--)
+ return;
+
+ if (datagram->state != EC_DATAGRAM_RECEIVED) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Failed to receive VoE read datagram: ");
+ ec_datagram_print_state(datagram);
+ return;
+ }
+
+ if (datagram->working_counter == 0) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_DBG(slave, 1, "Slave did not send VoE data.\n");
+ return;
+ }
+
+ if (datagram->working_counter != 1) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_WARN(slave, "Reception of VoE read response failed: ");
+ ec_datagram_print_wc_error(datagram);
+ return;
+ }
+
+ if (!(data = ec_slave_mbox_fetch(slave, datagram,
+ &mbox_prot, &rec_size))) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ return;
+ }
+
+ if (mbox_prot != EC_MBOX_TYPE_VOE) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_WARN(slave, "Received mailbox protocol 0x%02X"
+ " as response.\n", mbox_prot);
+ ec_print_data(data, rec_size);
+ return;
+ }
+
+ if (rec_size < EC_VOE_HEADER_SIZE) {
+ voe->state = ec_voe_handler_state_error;
+ voe->request_state = EC_INT_REQUEST_FAILURE;
+ EC_SLAVE_ERR(slave, "Received VoE header is"
+ " incomplete (%zu bytes)!\n", rec_size);
+ return;
+ }
+
+ if (master->debug_level) {
+ EC_CONFIG_DBG(voe->config, 1, "VoE data:\n");
+ ec_print_data(data, rec_size);
+ }
+
+ voe->data_size = rec_size - EC_VOE_HEADER_SIZE;
+ voe->request_state = EC_INT_REQUEST_SUCCESS;
+ voe->state = ec_voe_handler_state_end; // success
+}
+
+/*****************************************************************************/
+
+/** Successful termination state function.
+ */
+void ec_voe_handler_state_end(ec_voe_handler_t *voe)
+{
+}
+
+/*****************************************************************************/
+
+/** Failure termination state function.
+ */
+void ec_voe_handler_state_error(ec_voe_handler_t *voe)
+{
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_voe_handler_send_header);
+EXPORT_SYMBOL(ecrt_voe_handler_received_header);
+EXPORT_SYMBOL(ecrt_voe_handler_data);
+EXPORT_SYMBOL(ecrt_voe_handler_data_size);
+EXPORT_SYMBOL(ecrt_voe_handler_read);
+EXPORT_SYMBOL(ecrt_voe_handler_write);
+EXPORT_SYMBOL(ecrt_voe_handler_execute);
+
+/** \endcond */
+
+/*****************************************************************************/
diff --git a/net/ethercat/master/voe_handler.h b/net/ethercat/master/voe_handler.h
new file mode 100644
index 000000000000..e1b7eff80616
--- /dev/null
+++ b/net/ethercat/master/voe_handler.h
@@ -0,0 +1,73 @@
+/******************************************************************************
+ *
+ * $Id$
+ *
+ * Copyright (C) 2006-2008 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
+ Vendor specific over EtherCAT protocol handler.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_VOE_HANDLER_H__
+#define __EC_VOE_HANDLER_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+#include "datagram.h"
+
+/*****************************************************************************/
+
+/** Vendor specific over EtherCAT handler.
+ */
+struct ec_voe_handler {
+ struct list_head list; /**< List item. */
+ ec_slave_config_t *config; /**< Parent slave configuration. */
+ ec_datagram_t datagram; /**< State machine datagram. */
+ uint32_t vendor_id; /**< Vendor ID for the header. */
+ uint16_t vendor_type; /**< Vendor type for the header. */
+ size_t data_size; /**< Size of VoE data. */
+ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means writing to
+ the slave, EC_DIR_INPUT means reading from the
+ slave. */
+ void (*state)(ec_voe_handler_t *); /**< State function */
+ ec_internal_request_state_t request_state; /**< Handler state. */
+ unsigned int retries; /**< retries upon datagram timeout */
+ unsigned long jiffies_start; /**< Timestamp for timeout calculation. */
+};
+
+/*****************************************************************************/
+
+int ec_voe_handler_init(ec_voe_handler_t *, ec_slave_config_t *, size_t);
+void ec_voe_handler_clear(ec_voe_handler_t *);
+size_t ec_voe_handler_mem_size(const ec_voe_handler_t *);
+
+/*****************************************************************************/
+
+#endif