Re-worked and seamlessly integrated RTDM interface. stable-1.5
authorFlorian Pose <fp@igh-essen.com>
Thu, 20 Sep 2012 15:28:25 +0200
branchstable-1.5
changeset 2433 3bdd7a747fae
parent 2432 f4313f5aba88
child 2434 fa52128477f6
Re-worked and seamlessly integrated RTDM interface.
.hgignore
FEATURES
configure.ac
examples/Kbuild.in
examples/Makefile.am
examples/rtai_rtdm/Makefile.am
examples/rtai_rtdm/main.c
examples/xenomai/Makefile.am
examples/xenomai/main.c
examples/xenomai_posix/Makefile.am
examples/xenomai_posix/main.c
include/ecrt.h
lib/Makefile.am
lib/common.c
lib/domain.c
lib/ioctl.h
lib/master.c
lib/sdo_request.c
lib/slave_config.c
lib/voe_handler.c
master/Kbuild.in
master/Makefile.am
master/cdev.c
master/ioctl.c
master/ioctl.h
master/master.c
master/master.h
master/rtdm-ioctl.c
master/rtdm.c
master/rtdm.h
--- a/.hgignore	Thu Sep 20 09:20:51 2012 +0200
+++ b/.hgignore	Thu Sep 20 15:28:25 2012 +0200
@@ -100,12 +100,18 @@
 examples/rtai/Kbuild
 examples/rtai/Makefile
 examples/rtai/Makefile.in
+examples/rtai_rtdm/.libs
+examples/rtai_rtdm/ec_rtai_rtdm_example
 examples/user/.deps
 examples/user/.libs
 examples/user/Makefile
 examples/user/Makefile.in
 examples/user/TAGS
 examples/user/ec_user_example
+examples/xenomai/.libs
+examples/xenomai/ec_xenomai_example
+examples/xenomai_posix/.libs
+examples/xenomai_posix/ec_xenomai_posix_example
 include/Makefile
 include/Makefile.in
 include/TAGS
@@ -115,6 +121,7 @@
 lib/Makefile.in
 lib/TAGS
 lib/libethercat.la
+lib/libethercat_rtdm.la
 libtool
 m4/Makefile
 m4/Makefile.in
--- a/FEATURES	Thu Sep 20 09:20:51 2012 +0200
+++ b/FEATURES	Thu Sep 20 15:28:25 2012 +0200
@@ -27,6 +27,7 @@
 
 * Supports any realtime environment through independent architecture.
   - RTAI, Xenomai, RT-Preempt, etc.
+  - RTDM Interface for userspace realtime enviroments
   - Operation possible without any realtime extension at all.
 
 * Common API for Realtime-Applications in kernel- and userspace.
--- a/configure.ac	Thu Sep 20 09:20:51 2012 +0200
+++ b/configure.ac	Thu Sep 20 15:28:25 2012 +0200
@@ -426,13 +426,15 @@
 AC_ARG_WITH([rtai-dir],
     AC_HELP_STRING(
         [--with-rtai-dir=<DIR>],
-        [RTAI path (only for RTAI examples)]
+        [RTAI path, for RTDM interface and RTAI examples]
     ),
     [
         rtaidir=[$withval]
+        rtai=1
     ],
     [
         rtaidir=""
+        rtai=0
     ]
 )
 
@@ -445,9 +447,99 @@
         AC_MSG_ERROR([no RTAI installation found in ${rtaidir}!])
     fi
     AC_MSG_RESULT([$rtaidir])
+
+    rtai_lxrt_cflags=`$rtaidir/bin/rtai-config --lxrt-cflags`
+    rtai_lxrt_ldflags=`$rtaidir/bin/rtai-config --lxrt-ldflags`
 fi
 
 AC_SUBST(RTAI_DIR,[$rtaidir])
+AM_CONDITIONAL(ENABLE_RTAI, test "x$rtai" = "x1")
+AC_SUBST(ENABLE_RTAI,[$rtai])
+
+AC_SUBST(RTAI_LXRT_CFLAGS,[$rtai_lxrt_cflags])
+AC_SUBST(RTAI_LXRT_LDFLAGS,[$rtai_lxrt_ldflags])
+
+#------------------------------------------------------------------------------
+# Xenomai path (optional)
+#------------------------------------------------------------------------------
+
+AC_ARG_WITH([xenomai-dir],
+    AC_HELP_STRING(
+        [--with-xenomai-dir=<DIR>],
+        [Xenomai path, for RTDM interface and Xenomai examples]
+    ),
+    [
+        xenomaidir=[$withval]
+        xeno=1
+    ],
+    [
+        xenomaidir=""
+        xeno=0
+    ]
+)
+
+AC_MSG_CHECKING([for Xenomai path])
+
+if test -z "${xenomaidir}"; then
+    AC_MSG_RESULT([not specified.])
+else
+    if test \! -r ${xenomaidir}/include/xeno_config.h; then
+        AC_MSG_ERROR([no Xenomai installation found in ${xenomaidir}!])
+    fi
+    AC_MSG_RESULT([$xenomaidir])
+
+    xeno_native_cflags=`$xenomaidir/bin/xeno-config --skin native --cflags`
+    xeno_native_ldflags=`$xenomaidir/bin/xeno-config --skin native --ldflags`
+    xeno_posix_cflags=`$xenomaidir/bin/xeno-config --skin posix --cflags`
+    xeno_posix_ldflags=`$xenomaidir/bin/xeno-config --skin posix --ldflags`
+    xeno_rtdm_cflags=`$xenomaidir/bin/xeno-config --skin rtdm --cflags`
+    xeno_rtdm_ldflags=`$xenomaidir/bin/xeno-config --skin rtdm --ldflags`
+fi
+
+AC_SUBST(XENOMAI_DIR,[$xenomaidir])
+AM_CONDITIONAL(ENABLE_XENOMAI, test "x$xeno" = "x1")
+AC_SUBST(ENABLE_XENOMAI,[$xeno])
+
+AC_SUBST(XENOMAI_NATIVE_CFLAGS,[$xeno_native_cflags])
+AC_SUBST(XENOMAI_NATIVE_LDFLAGS,[$xeno_native_ldflags])
+AC_SUBST(XENOMAI_POSIX_CFLAGS,[$xeno_posix_cflags])
+AC_SUBST(XENOMAI_POSIX_LDFLAGS,[$xeno_posix_ldflags])
+AC_SUBST(XENOMAI_RTDM_CFLAGS,[$xeno_rtdm_cflags])
+AC_SUBST(XENOMAI_RTDM_LDFLAGS,[$xeno_rtdm_ldflags])
+
+#------------------------------------------------------------------------------
+# RTDM interface (optional)
+#------------------------------------------------------------------------------
+
+AC_ARG_ENABLE([rtdm],
+    AC_HELP_STRING(
+        [--enable-rtdm],
+        [Enable RTDM interface, depends on RTAI or Xenomai]
+    ),
+    [
+        case "${enableval}" in
+            yes) rtdm=1
+                ;;
+            no) rtdm=0
+                ;;
+            *) AC_MSG_ERROR([Invalid value for --enable-rtdm])
+                ;;
+        esac
+    ],
+    [rtdm=0]
+)
+
+AC_MSG_CHECKING([whether to build RTDM interface])
+
+if test "x${rtdm}" = "x1"; then
+    AC_DEFINE([EC_RTDM], [1], [RTDM interface enabled])
+    AC_MSG_RESULT([yes])
+else
+    AC_MSG_RESULT([no])
+fi
+
+AM_CONDITIONAL(ENABLE_RTDM, test "x$rtdm" = "x1")
+AC_SUBST(ENABLE_RTDM,[$rtdm])
 
 #------------------------------------------------------------------------------
 # Debug interface
@@ -711,9 +803,12 @@
         examples/mini/Makefile
         examples/rtai/Kbuild
         examples/rtai/Makefile
+        examples/rtai_rtdm/Makefile
         examples/tty/Kbuild
         examples/tty/Makefile
         examples/user/Makefile
+        examples/xenomai/Makefile
+        examples/xenomai_posix/Makefile
         include/Makefile
         lib/Makefile
         m4/Makefile
--- a/examples/Kbuild.in	Thu Sep 20 09:20:51 2012 +0200
+++ b/examples/Kbuild.in	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
 #
 #  $Id$
 #
-#  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+#  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
 #
 #  This file is part of the IgH EtherCAT Master.
 #
@@ -35,4 +35,8 @@
 	obj-m += tty/
 endif
 
+ifeq (@ENABLE_RTAI@,1)
+	obj-m += rtai/ dc_rtai/
+endif
+
 #------------------------------------------------------------------------------
--- a/examples/Makefile.am	Thu Sep 20 09:20:51 2012 +0200
+++ b/examples/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
 #
 #  $Id$
 #
-#  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+#  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
 #
 #  This file is part of the IgH EtherCAT Master.
 #
@@ -40,13 +40,29 @@
 	tty
 endif
 
+if ENABLE_RTDM
+if ENABLE_XENOMAI
+SUBDIRS += \
+	xenomai \
+	xenomai_posix
+endif
+
+if ENABLE_RTAI
+SUBDIRS += \
+	rtai_rtdm
+endif
+endif
+
 DIST_SUBDIRS = \
 	dc_rtai \
 	dc_user \
 	mini \
 	rtai \
+	rtai_rtdm \
 	tty \
-	user
+	user \
+	xenomai \
+	xenomai_posix
 
 EXTRA_DIST = \
 	Kbuild.in
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rtai_rtdm/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,43 @@
+#------------------------------------------------------------------------------
+#
+#  $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.
+#
+#------------------------------------------------------------------------------
+
+noinst_PROGRAMS = ec_rtai_rtdm_example
+
+ec_rtai_rtdm_example_SOURCES = main.c
+
+ec_rtai_rtdm_example_CFLAGS = \
+	-Wall \
+	-I$(top_srcdir)/include \
+	$(RTAI_LXRT_CFLAGS)
+
+ec_rtai_rtdm_example_LDFLAGS = \
+	$(RTAI_LXRT_LDFLAGS) -llxrt -lrtdm \
+	-L$(top_builddir)/lib/.libs -lethercat_rtdm
+
+#------------------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/rtai_rtdm/main.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,289 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C)      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 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, 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.
+ *
+ *****************************************************************************/
+
+#include <sched.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <signal.h>
+
+#include <rtai_lxrt.h>
+#include <rtdm/rtdm.h>
+
+#include "ecrt.h"
+
+#define rt_printf(X, Y)
+
+#define NSEC_PER_SEC 1000000000
+
+RT_TASK *task;
+
+const static unsigned int cycle_us = 1000; /* 1 ms */
+
+static int run = 1;
+
+/****************************************************************************/
+
+// EtherCAT
+static ec_master_t *master = NULL;
+static ec_master_state_t master_state = {};
+
+static ec_domain_t *domain1 = NULL;
+static ec_domain_state_t domain1_state = {};
+
+static uint8_t *domain1_pd = NULL;
+
+static ec_slave_config_t *sc_dig_out_01 = NULL;
+
+/****************************************************************************/
+
+// process data
+
+#define BusCoupler01_Pos  0, 0
+#define DigOutSlave01_Pos 0, 1
+
+#define Beckhoff_EK1100 0x00000002, 0x044c2c52
+#define Beckhoff_EL2004 0x00000002, 0x07d43052
+
+// offsets for PDO entries
+static unsigned int off_dig_out0 = 0;
+
+// process data
+
+const static ec_pdo_entry_reg_t domain1_regs[] = {
+   {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL},
+   {}
+};
+
+/****************************************************************************/
+
+/* Slave 1, "EL2004"
+ * Vendor ID:       0x00000002
+ * Product code:    0x07d43052
+ * Revision number: 0x00100000
+ */
+
+ec_pdo_entry_info_t slave_1_pdo_entries[] = {
+   {0x7000, 0x01, 1}, /* Output */
+   {0x7010, 0x01, 1}, /* Output */
+   {0x7020, 0x01, 1}, /* Output */
+   {0x7030, 0x01, 1}, /* Output */
+};
+
+ec_pdo_info_t slave_1_pdos[] = {
+   {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */
+   {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */
+   {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */
+   {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */
+};
+
+ec_sync_info_t slave_1_syncs[] = {
+   {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE},
+   {0xff}
+};
+
+/*****************************************************************************
+ * Realtime task
+ ****************************************************************************/
+
+void rt_check_domain_state(void)
+{
+    ec_domain_state_t ds = {};
+
+    ecrt_domain_state(domain1, &ds);
+
+    if (ds.working_counter != domain1_state.working_counter) {
+        rt_printf("Domain1: WC %u.\n", ds.working_counter);
+    }
+
+    if (ds.wc_state != domain1_state.wc_state) {
+        rt_printf("Domain1: State %u.\n", ds.wc_state);
+    }
+
+    domain1_state = ds;
+}
+
+/****************************************************************************/
+
+void rt_check_master_state(void)
+{
+    ec_master_state_t ms;
+
+    ecrt_master_state(master, &ms);
+
+    if (ms.slaves_responding != master_state.slaves_responding) {
+        rt_printf("%u slave(s).\n", ms.slaves_responding);
+    }
+
+    if (ms.al_states != master_state.al_states) {
+        rt_printf("AL states: 0x%02X.\n", ms.al_states);
+    }
+
+    if (ms.link_up != master_state.link_up) {
+        rt_printf("Link is %s.\n", ms.link_up ? "up" : "down");
+    }
+
+    master_state = ms;
+}
+
+/****************************************************************************/
+
+void my_cyclic(void)
+{
+    int cycle_counter = 0;
+    int period;
+    unsigned int blink = 0;
+
+    rt_set_periodic_mode();
+    period = (int) nano2count((RTIME) cycle_us * 1000);
+    start_rt_timer(period);
+    rt_make_hard_real_time();
+    rt_task_make_periodic(task, rt_get_time() + 10 * period, period);
+
+    while (run) {
+        rt_task_wait_period();
+
+        cycle_counter++;
+
+        // receive EtherCAT
+        ecrt_master_receive(master);
+        ecrt_domain_process(domain1);
+
+        rt_check_domain_state();
+
+        if (!(cycle_counter % 1000)) {
+            rt_check_master_state();
+        }
+
+        if (!(cycle_counter % 200)) {
+            blink = !blink;
+        }
+
+        EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x00 : 0x0F);
+
+        // send process data
+        ecrt_domain_queue(domain1);
+        ecrt_master_send(master);
+    }
+
+    rt_make_soft_real_time();
+    stop_rt_timer();
+}
+
+/****************************************************************************
+ * Signal handler
+ ***************************************************************************/
+
+void signal_handler(int sig)
+{
+    run = 0;
+}
+
+/****************************************************************************
+ * Main function
+ ***************************************************************************/
+
+int main(int argc, char *argv[])
+{
+    ec_slave_config_t *sc;
+
+    signal(SIGTERM, signal_handler);
+    signal(SIGINT, signal_handler);
+
+    mlockall(MCL_CURRENT | MCL_FUTURE);
+
+    printf("Requesting master...\n");
+    master = ecrt_request_master(0);
+    if (!master) {
+        return -1;
+    }
+
+    domain1 = ecrt_master_create_domain(master);
+    if (!domain1) {
+        return -1;
+    }
+
+    printf("Creating slave configurations...\n");
+
+    // Create configuration for bus coupler
+    sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100);
+    if (!sc) {
+        return -1;
+    }
+
+    sc_dig_out_01 =
+        ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004);
+    if (!sc_dig_out_01) {
+        fprintf(stderr, "Failed to get slave configuration.\n");
+        return -1;
+    }
+
+    if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) {
+        fprintf(stderr, "Failed to configure PDOs.\n");
+        return -1;
+    }
+
+    if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
+        fprintf(stderr, "PDO entry registration failed!\n");
+        return -1;
+    }
+
+    printf("Activating master...\n");
+    if (ecrt_master_activate(master)) {
+        return -1;
+    }
+
+    if (!(domain1_pd = ecrt_domain_data(domain1))) {
+        fprintf(stderr, "Failed to get domain data pointer.\n");
+        return -1;
+    }
+
+    /* Create cyclic RT-thread */
+    struct sched_param param;
+    param.sched_priority = sched_get_priority_max(SCHED_FIFO) - 1;
+    if (sched_setscheduler(0, SCHED_FIFO, &param) == -1) {
+        puts("ERROR IN SETTING THE SCHEDULER");
+        perror("errno");
+        return -1;
+    }
+
+    task = rt_task_init(nam2num("ec_rtai_rtdm_example"),
+            0 /* priority */, 0 /* stack size */, 0 /* msg size */);
+
+    my_cyclic();
+
+    rt_task_delete(task);
+
+    printf("End of Program\n");
+    ecrt_release_master(master);
+
+    return 0;
+}
+
+/****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/xenomai/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,45 @@
+#------------------------------------------------------------------------------
+#
+#  $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.
+#
+#------------------------------------------------------------------------------
+
+noinst_PROGRAMS = ec_xenomai_example
+
+ec_xenomai_example_SOURCES = main.c
+
+ec_xenomai_example_CFLAGS = \
+	-Wall \
+	-I$(top_srcdir)/include \
+	$(XENOMAI_NATIVE_CFLAGS) \
+	$(XENOMAI_RTDM_CFLAGS)
+
+ec_xenomai_example_LDFLAGS = \
+	-L$(top_builddir)/lib/.libs -lethercat_rtdm \
+	$(XENOMAI_NATIVE_LDFLAGS) \
+	$(XENOMAI_RTDM_LDFLAGS)
+
+#------------------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/xenomai/main.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,292 @@
+/******************************************************************************
+ *
+ *  $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 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, 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.
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <rtdm/rtdm.h>
+#include <native/task.h>
+#include <native/sem.h>
+#include <native/mutex.h>
+#include <native/timer.h>
+#include <rtdk.h>
+#include <pthread.h>
+
+#include "ecrt.h"
+
+RT_TASK my_task;
+
+static int run = 1;
+
+/****************************************************************************/
+
+// EtherCAT
+static ec_master_t *master = NULL;
+static ec_master_state_t master_state = {};
+
+static ec_domain_t *domain1 = NULL;
+static ec_domain_state_t domain1_state = {};
+
+static uint8_t *domain1_pd = NULL;
+
+static ec_slave_config_t *sc_dig_out_01 = NULL;
+
+/****************************************************************************/
+
+// process data
+
+#define BusCoupler01_Pos  0, 0
+#define DigOutSlave01_Pos 0, 1
+
+#define Beckhoff_EK1100 0x00000002, 0x044c2c52
+#define Beckhoff_EL2004 0x00000002, 0x07d43052
+
+// offsets for PDO entries
+static unsigned int off_dig_out0 = 0;
+
+// process data
+
+const static ec_pdo_entry_reg_t domain1_regs[] = {
+   {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL},
+   {}
+};
+
+/****************************************************************************/
+
+/* Slave 1, "EL2004"
+ * Vendor ID:       0x00000002
+ * Product code:    0x07d43052
+ * Revision number: 0x00100000
+ */
+
+ec_pdo_entry_info_t slave_1_pdo_entries[] = {
+   {0x7000, 0x01, 1}, /* Output */
+   {0x7010, 0x01, 1}, /* Output */
+   {0x7020, 0x01, 1}, /* Output */
+   {0x7030, 0x01, 1}, /* Output */
+};
+
+ec_pdo_info_t slave_1_pdos[] = {
+   {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */
+   {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */
+   {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */
+   {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */
+};
+
+ec_sync_info_t slave_1_syncs[] = {
+   {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE},
+   {0xff}
+};
+
+/*****************************************************************************
+ * Realtime task
+ ****************************************************************************/
+
+void rt_check_domain_state(void)
+{
+    ec_domain_state_t ds = {};
+
+	ecrt_domain_state(domain1, &ds);
+
+    if (ds.working_counter != domain1_state.working_counter) {
+        rt_printf("Domain1: WC %u.\n", ds.working_counter);
+    }
+
+    if (ds.wc_state != domain1_state.wc_state) {
+        rt_printf("Domain1: State %u.\n", ds.wc_state);
+    }
+
+    domain1_state = ds;
+}
+
+/****************************************************************************/
+
+void rt_check_master_state(void)
+{
+    ec_master_state_t ms;
+
+	ecrt_master_state(master, &ms);
+
+    if (ms.slaves_responding != master_state.slaves_responding) {
+        rt_printf("%u slave(s).\n", ms.slaves_responding);
+    }
+
+    if (ms.al_states != master_state.al_states) {
+        rt_printf("AL states: 0x%02X.\n", ms.al_states);
+    }
+
+    if (ms.link_up != master_state.link_up) {
+        rt_printf("Link is %s.\n", ms.link_up ? "up" : "down");
+    }
+
+    master_state = ms;
+}
+
+/****************************************************************************/
+
+void my_task_proc(void *arg)
+{
+	int cycle_counter = 0;
+    unsigned int blink = 0;
+
+	rt_task_set_periodic(NULL, TM_NOW, 1000000); // ns
+
+	while (run) {
+		rt_task_wait_period(NULL);
+
+		cycle_counter++;
+
+		// receive EtherCAT frames
+		ecrt_master_receive(master);
+		ecrt_domain_process(domain1);
+
+		rt_check_domain_state();
+
+		if (!(cycle_counter % 1000)) {
+			rt_check_master_state();
+		}
+
+		if (!(cycle_counter % 200)) {
+			blink = !blink;
+		}
+
+		EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x0 : 0x0F);
+
+		// send process data
+		ecrt_domain_queue(domain1);
+		ecrt_master_send(master);
+	}
+}
+
+/****************************************************************************
+ * Signal handler
+ ***************************************************************************/
+
+void signal_handler(int sig)
+{
+    run = 0;
+}
+
+/****************************************************************************
+ * Main function
+ ***************************************************************************/
+
+int main(int argc, char *argv[])
+{
+    ec_slave_config_t *sc;
+    int ret;
+
+    /* Perform auto-init of rt_print buffers if the task doesn't do so */
+    rt_print_auto_init(1);
+
+    signal(SIGTERM, signal_handler);
+    signal(SIGINT, signal_handler);
+
+    mlockall(MCL_CURRENT | MCL_FUTURE);
+
+    printf("Requesting master...\n");
+    master = ecrt_request_master(0);
+    if (!master) {
+        return -1;
+    }
+
+    domain1 = ecrt_master_create_domain(master);
+    if (!domain1) {
+        return -1;
+    }
+
+    printf("Creating slave configurations...\n");
+
+    // Create configuration for bus coupler
+    sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100);
+    if (!sc) {
+        return -1;
+    }
+
+    sc_dig_out_01 =
+        ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004);
+    if (!sc_dig_out_01) {
+        fprintf(stderr, "Failed to get slave configuration.\n");
+        return -1;
+    }
+
+    if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) {
+        fprintf(stderr, "Failed to configure PDOs.\n");
+        return -1;
+    }
+
+    if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
+        fprintf(stderr, "PDO entry registration failed!\n");
+        return -1;
+    }
+
+    printf("Activating master...\n");
+    if (ecrt_master_activate(master)) {
+        return -1;
+    }
+
+    if (!(domain1_pd = ecrt_domain_data(domain1))) {
+        fprintf(stderr, "Failed to get domain data pointer.\n");
+        return -1;
+    }
+
+    ret = rt_task_create(&my_task, "my_task", 0, 80, T_FPU);
+    if (ret < 0) {
+        fprintf(stderr, "Failed to create task: %s\n", strerror(-ret));
+        return -1;
+    }
+
+    printf("Starting my_task...\n");
+    ret = rt_task_start(&my_task, &my_task_proc, NULL);
+    if (ret < 0) {
+        fprintf(stderr, "Failed to start task: %s\n", strerror(-ret));
+        return -1;
+    }
+
+	while (run) {
+		sched_yield();
+	}
+
+    printf("Deleting realtime task...\n");
+    rt_task_delete(&my_task);
+
+    printf("End of Program\n");
+    ecrt_release_master(master);
+
+    return 0;
+}
+
+/****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/xenomai_posix/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,45 @@
+#------------------------------------------------------------------------------
+#
+#  $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.
+#
+#------------------------------------------------------------------------------
+
+noinst_PROGRAMS = ec_xenomai_posix_example
+
+ec_xenomai_posix_example_SOURCES = main.c
+
+ec_xenomai_posix_example_CFLAGS = \
+	-Wall \
+	-I$(top_srcdir)/include \
+	$(XENOMAI_POSIX_CFLAGS) \
+	$(XENOMAI_RTDM_CFLAGS)
+
+ec_xenomai_posix_example_LDFLAGS = \
+	-L$(top_builddir)/lib/.libs -lethercat_rtdm \
+	$(XENOMAI_POSIX_LDFLAGS) \
+	$(XENOMAI_RTDM_LDFLAGS)
+
+#------------------------------------------------------------------------------
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/xenomai_posix/main.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,303 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C)      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 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, 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.
+ *
+ *****************************************************************************/
+
+#include <errno.h>
+#include <mqueue.h>
+#include <signal.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <time.h>
+
+#include <rtdm/rtdm.h>
+#include <rtdk.h>
+
+#include "ecrt.h"
+
+#define NSEC_PER_SEC 1000000000
+
+static unsigned int cycle_us = 1000; /* 1 ms */
+
+static pthread_t cyclic_thread;
+
+static int run = 1;
+
+/****************************************************************************/
+
+// EtherCAT
+static ec_master_t *master = NULL;
+static ec_master_state_t master_state = {};
+
+static ec_domain_t *domain1 = NULL;
+static ec_domain_state_t domain1_state = {};
+
+static uint8_t *domain1_pd = NULL;
+
+static ec_slave_config_t *sc_dig_out_01 = NULL;
+
+/****************************************************************************/
+
+// process data
+
+#define BusCoupler01_Pos  0, 0
+#define DigOutSlave01_Pos 0, 1
+
+#define Beckhoff_EK1100 0x00000002, 0x044c2c52
+#define Beckhoff_EL2004 0x00000002, 0x07d43052
+
+// offsets for PDO entries
+static unsigned int off_dig_out0 = 0;
+
+// process data
+
+const static ec_pdo_entry_reg_t domain1_regs[] = {
+   {DigOutSlave01_Pos, Beckhoff_EL2004, 0x7000, 0x01, &off_dig_out0, NULL},
+   {}
+};
+
+/****************************************************************************/
+
+/* Slave 1, "EL2004"
+ * Vendor ID:       0x00000002
+ * Product code:    0x07d43052
+ * Revision number: 0x00100000
+ */
+
+ec_pdo_entry_info_t slave_1_pdo_entries[] = {
+   {0x7000, 0x01, 1}, /* Output */
+   {0x7010, 0x01, 1}, /* Output */
+   {0x7020, 0x01, 1}, /* Output */
+   {0x7030, 0x01, 1}, /* Output */
+};
+
+ec_pdo_info_t slave_1_pdos[] = {
+   {0x1600, 1, slave_1_pdo_entries + 0}, /* Channel 1 */
+   {0x1601, 1, slave_1_pdo_entries + 1}, /* Channel 2 */
+   {0x1602, 1, slave_1_pdo_entries + 2}, /* Channel 3 */
+   {0x1603, 1, slave_1_pdo_entries + 3}, /* Channel 4 */
+};
+
+ec_sync_info_t slave_1_syncs[] = {
+   {0, EC_DIR_OUTPUT, 4, slave_1_pdos + 0, EC_WD_ENABLE},
+   {0xff}
+};
+
+/*****************************************************************************
+ * Realtime task
+ ****************************************************************************/
+
+void rt_check_domain_state(void)
+{
+    ec_domain_state_t ds = {};
+
+	ecrt_domain_state(domain1, &ds);
+
+    if (ds.working_counter != domain1_state.working_counter) {
+        rt_printf("Domain1: WC %u.\n", ds.working_counter);
+    }
+
+    if (ds.wc_state != domain1_state.wc_state) {
+        rt_printf("Domain1: State %u.\n", ds.wc_state);
+    }
+
+    domain1_state = ds;
+}
+
+/****************************************************************************/
+
+void rt_check_master_state(void)
+{
+    ec_master_state_t ms;
+
+	ecrt_master_state(master, &ms);
+
+    if (ms.slaves_responding != master_state.slaves_responding) {
+        rt_printf("%u slave(s).\n", ms.slaves_responding);
+    }
+
+    if (ms.al_states != master_state.al_states) {
+        rt_printf("AL states: 0x%02X.\n", ms.al_states);
+    }
+
+    if (ms.link_up != master_state.link_up) {
+        rt_printf("Link is %s.\n", ms.link_up ? "up" : "down");
+    }
+
+    master_state = ms;
+}
+
+/****************************************************************************/
+
+void *my_thread(void *arg)
+{
+    struct timespec next_period;
+    int cycle_counter = 0;
+	unsigned int blink = 0;
+
+    clock_gettime(CLOCK_REALTIME, &next_period);
+
+    while (run) {
+        next_period.tv_nsec += cycle_us * 1000;
+        while (next_period.tv_nsec >= NSEC_PER_SEC) {
+			next_period.tv_nsec -= NSEC_PER_SEC;
+			next_period.tv_sec++;
+		}
+
+        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &next_period, NULL);
+
+        cycle_counter++;
+
+        // receive EtherCAT
+        ecrt_master_receive(master);
+        ecrt_domain_process(domain1);
+
+        rt_check_domain_state();
+
+        if (!(cycle_counter % 1000)) {
+			rt_check_master_state();
+		}
+
+        if (!(cycle_counter % 200)) {
+			blink = !blink;
+		}
+
+        EC_WRITE_U8(domain1_pd + off_dig_out0, blink ? 0x0 : 0x0F);
+
+        // send process data
+        ecrt_domain_queue(domain1);
+        ecrt_master_send(master);
+    }
+
+    return NULL;
+}
+
+/****************************************************************************
+ * Signal handler
+ ***************************************************************************/
+
+void signal_handler(int sig)
+{
+    run = 0;
+}
+
+/****************************************************************************
+ * Main function
+ ***************************************************************************/
+
+int main(int argc, char *argv[])
+{
+    ec_slave_config_t *sc;
+    int ret;
+
+    signal(SIGTERM, signal_handler);
+    signal(SIGINT, signal_handler);
+
+    mlockall(MCL_CURRENT | MCL_FUTURE);
+
+    printf("Requesting master...\n");
+    master = ecrt_request_master(0);
+    if (!master) {
+        return -1;
+    }
+
+    domain1 = ecrt_master_create_domain(master);
+    if (!domain1) {
+        return -1;
+    }
+
+    printf("Creating slave configurations...\n");
+
+    // Create configuration for bus coupler
+    sc = ecrt_master_slave_config(master, BusCoupler01_Pos, Beckhoff_EK1100);
+    if (!sc) {
+        return -1;
+    }
+
+    sc_dig_out_01 =
+        ecrt_master_slave_config(master, DigOutSlave01_Pos, Beckhoff_EL2004);
+    if (!sc_dig_out_01) {
+        fprintf(stderr, "Failed to get slave configuration.\n");
+        return -1;
+    }
+
+    if (ecrt_slave_config_pdos(sc_dig_out_01, EC_END, slave_1_syncs)) {
+        fprintf(stderr, "Failed to configure PDOs.\n");
+        return -1;
+    }
+
+    if (ecrt_domain_reg_pdo_entry_list(domain1, domain1_regs)) {
+        fprintf(stderr, "PDO entry registration failed!\n");
+        return -1;
+    }
+
+    printf("Activating master...\n");
+    if (ecrt_master_activate(master)) {
+        return -1;
+    }
+
+    if (!(domain1_pd = ecrt_domain_data(domain1))) {
+        fprintf(stderr, "Failed to get domain data pointer.\n");
+        return -1;
+    }
+
+    /* Create cyclic RT-thread */
+    struct sched_param param = { .sched_priority = 82 };
+
+    pthread_attr_t thattr;
+    pthread_attr_init(&thattr);
+    pthread_attr_setdetachstate(&thattr, PTHREAD_CREATE_JOINABLE);
+    pthread_attr_setinheritsched(&thattr, PTHREAD_EXPLICIT_SCHED);
+    pthread_attr_setschedpolicy(&thattr, SCHED_FIFO);
+    pthread_setschedparam(cyclic_thread, SCHED_FIFO, &param);
+
+    ret = pthread_create(&cyclic_thread, &thattr, &my_thread, NULL);
+    if (ret) {
+        fprintf(stderr, "%s: pthread_create cyclic task failed\n",
+                strerror(-ret));
+		return 1;
+    }
+
+	while (run) {
+		sched_yield();
+	}
+
+    pthread_join(cyclic_thread, NULL);
+
+    printf("End of Program\n");
+    ecrt_release_master(master);
+
+    return 0;
+}
+
+/****************************************************************************/
--- a/include/ecrt.h	Thu Sep 20 09:20:51 2012 +0200
+++ b/include/ecrt.h	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -781,7 +781,7 @@
  * error occurred.
  *
  * \retval  0 Success.
- * \retval -1 An error occured.
+ * \retval <0 Error code.
  */
 int ecrt_master_write_idn(
         ec_master_t *master, /**< EtherCAT master. */
@@ -800,7 +800,7 @@
  * error occurred.
  *
  * \retval  0 Success.
- * \retval -1 An error occured.
+ * \retval <0 Error code.
  */
 int ecrt_master_read_idn(
         ec_master_t *master, /**< EtherCAT master. */
@@ -848,6 +848,10 @@
         );
 
 /** Set interval between calls to ecrt_master_send().
+ *
+ * \retval 0 on success.
+ * \retval <0 Error code.
+ *
  */
 int ecrt_master_set_send_interval(
         ec_master_t *master, /**< EtherCAT master. */
--- a/lib/Makefile.am	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
 #
 #  $Id$
 #
-#  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+#  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
 #
 #  This file is part of the IgH EtherCAT master userspace library.
 #
@@ -32,8 +32,6 @@
 
 #------------------------------------------------------------------------------
 
-libethercat_la_CFLAGS = -I$(srcdir)/.. -fno-strict-aliasing -Wall
-libethercat_la_LDFLAGS = -version-info 1:0:0
 libethercat_la_SOURCES = \
 	common.c \
 	domain.c \
@@ -49,4 +47,29 @@
 	slave_config.h \
 	voe_handler.h
 
+libethercat_la_CFLAGS = -fno-strict-aliasing -Wall
+libethercat_la_LDFLAGS = -version-info 1:0:0
+
 #------------------------------------------------------------------------------
+
+if ENABLE_RTDM
+
+lib_LTLIBRARIES += libethercat_rtdm.la
+
+libethercat_rtdm_la_SOURCES = $(libethercat_la_SOURCES)
+libethercat_rtdm_la_CFLAGS = $(libethercat_la_CFLAGS) -DUSE_RTDM
+libethercat_rtdm_la_LDFLAGS = $(libethercat_la_LDFLAGS)
+
+if ENABLE_XENOMAI
+libethercat_rtdm_la_CFLAGS += $(XENOMAI_RTDM_CFLAGS)
+libethercat_rtdm_la_LDFLAGS += $(XENOMAI_RTDM_LDFLAGS)
+endif
+
+if ENABLE_RTAI
+libethercat_rtdm_la_CFLAGS += $(RTAI_LXRT_CFLAGS)
+libethercat_rtdm_la_LDFLAGS += $(RTAI_LXRT_LDFLAGS)
+endif
+
+endif
+
+#------------------------------------------------------------------------------
--- a/lib/common.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/common.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -33,15 +33,13 @@
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <errno.h>
 #include <string.h>
-#include <sys/ioctl.h>
 #include <sys/mman.h>
 #include <unistd.h>
 #include <sys/socket.h>
 
+#include "ioctl.h"
 #include "master.h"
-#include "master/ioctl.h"
 
 /*****************************************************************************/
 
@@ -75,6 +73,7 @@
     char path[MAX_PATH_LEN];
     ec_master_t *master = NULL;
     ec_ioctl_module_t module_data;
+    int ret;
 
     master = malloc(sizeof(ec_master_t));
     if (!master) {
@@ -87,17 +86,29 @@
     master->first_domain = NULL;
     master->first_config = NULL;
 
-    snprintf(path, MAX_PATH_LEN - 1, "/dev/EtherCAT%u", master_index);
+    snprintf(path, MAX_PATH_LEN - 1,
+#ifdef USE_RTDM
+            "EtherCAT%u",
+#else
+            "/dev/EtherCAT%u",
+#endif
+            master_index);
 
+#ifdef USE_RTDM
+    master->fd = rt_dev_open(path, O_RDWR);
+#else
     master->fd = open(path, O_RDWR);
-    if (master->fd == -1) {
-        fprintf(stderr, "Failed to open %s: %s\n", path, strerror(errno));
+#endif
+    if (EC_IOCTL_IS_ERROR(master->fd)) {
+        fprintf(stderr, "Failed to open %s: %s\n", path,
+                strerror(EC_IOCTL_ERRNO(master->fd)));
         goto out_clear;
     }
 
-    if (ioctl(master->fd, EC_IOCTL_MODULE, &module_data) < 0) {
+    ret = ioctl(master->fd, EC_IOCTL_MODULE, &module_data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get module information from %s: %s\n",
-                path, strerror(errno));
+                path, strerror(EC_IOCTL_ERRNO(ret)));
         goto out_clear;
     }
 
--- a/lib/domain.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/domain.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -35,15 +35,13 @@
 
 /*****************************************************************************/
 
-#include <sys/ioctl.h>
-#include <sys/mman.h>
 #include <stdio.h>
-#include <errno.h>
 #include <string.h>
+#include <errno.h> /* ENOENT */
 
+#include "ioctl.h"
 #include "domain.h"
 #include "master.h"
-#include "master/ioctl.h"
 
 /*****************************************************************************/
 
@@ -64,11 +62,11 @@
     for (reg = regs; reg->index; reg++) {
         if (!(sc = ecrt_master_slave_config(domain->master, reg->alias,
                         reg->position, reg->vendor_id, reg->product_code)))
-            return -1; // FIXME
+            return -ENOENT;
 
         if ((ret = ecrt_slave_config_reg_pdo_entry(sc, reg->index,
                         reg->subindex, domain, reg->bit_position)) < 0)
-            return -1; // FIXME
+            return ret;
 
         *reg->offset = ret;
     }
@@ -85,9 +83,9 @@
 
         offset = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_OFFSET,
                 domain->index);
-        if (offset == -1) {
+        if (EC_IOCTL_IS_ERROR(offset)) {
             fprintf(stderr, "Failed to get domain offset: %s\n",
-                    strerror(errno));
+                    strerror(EC_IOCTL_ERRNO(offset)));
             return NULL;
         }
 
@@ -101,9 +99,12 @@
 
 void ecrt_domain_process(ec_domain_t *domain)
 {
-    if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS,
-                domain->index) == -1) {
-        fprintf(stderr, "Failed to process domain: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_PROCESS, domain->index);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to process domain: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -111,9 +112,12 @@
 
 void ecrt_domain_queue(ec_domain_t *domain)
 {
-    if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_QUEUE,
-                domain->index) == -1) {
-        fprintf(stderr, "Failed to queue domain: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_QUEUE, domain->index);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to queue domain: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -122,13 +126,15 @@
 void ecrt_domain_state(const ec_domain_t *domain, ec_domain_state_t *state)
 {
     ec_ioctl_domain_state_t data;
+    int ret;
 
     data.domain_index = domain->index;
     data.state = state;
 
-    if (ioctl(domain->master->fd, EC_IOCTL_DOMAIN_STATE, &data) == -1) {
+    ret = ioctl(domain->master->fd, EC_IOCTL_DOMAIN_STATE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get domain state: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/ioctl.h	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,73 @@
+/******************************************************************************
+ *
+ *  $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.
+ *
+ *****************************************************************************/
+
+#ifndef __EC_LIB_IOCTL_H__
+#define __EC_LIB_IOCTL_H__
+
+/*****************************************************************************/
+
+#ifdef USE_RTDM
+#include <rtdm/rtdm.h>
+#else
+#include <sys/ioctl.h>
+#endif
+
+/*****************************************************************************/
+
+#include "master/ioctl.h"
+
+/*****************************************************************************/
+
+#ifdef USE_RTDM
+
+#define ioctl rt_dev_ioctl
+
+/* rt_dev_ioctl() returns negative error code */
+#define EC_IOCTL_IS_ERROR(X) ((X) < 0)
+#define EC_IOCTL_ERRNO(X) (-(X))
+
+#else
+
+#define ioctl ioctl
+
+/* libc's ioctl() always returns -1 on error and sets errno */
+#define EC_IOCTL_IS_ERROR(X) ((X) == -1)
+#define EC_IOCTL_ERRNO(X) (errno)
+
+#include <errno.h>
+
+#endif
+
+/*****************************************************************************/
+
+#endif /* __EC_LIB_IOCTL_H__ */
+
+/*****************************************************************************/
+
--- a/lib/master.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/master.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -30,25 +30,25 @@
 
 #include <unistd.h> /* close() */
 #include <stdlib.h>
-#include <sys/ioctl.h>
 #include <stdio.h>
 #include <errno.h>
 #include <string.h>
 #include <sys/mman.h>
 
+#include "ioctl.h"
 #include "master.h"
 #include "domain.h"
 #include "slave_config.h"
-#include "master/ioctl.h"
 
 /****************************************************************************/
 
 int ecrt_master_reserve(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_REQUEST, NULL) == -1) {
+    int ret = ioctl(master->fd, EC_IOCTL_REQUEST, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to reserve master: %s\n",
-                strerror(errno));
-        return -1;
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
     return 0;
 }
@@ -88,7 +88,11 @@
     ec_master_clear_config(master);
 
     if (master->fd != -1) {
+#if USE_RTDM
+        rt_dev_close(master->fd);
+#else
         close(master->fd);
+#endif
     }
 }
 
@@ -121,8 +125,9 @@
     }
 
     index = ioctl(master->fd, EC_IOCTL_CREATE_DOMAIN, NULL);
-    if (index == -1) {
-        fprintf(stderr, "Failed to create domain: %s\n", strerror(errno));
+    if (EC_IOCTL_IS_ERROR(index)) {
+        fprintf(stderr, "Failed to create domain: %s\n",
+                strerror(EC_IOCTL_ERRNO(index)));
         free(domain);
         return 0;
     }
@@ -160,6 +165,7 @@
 {
     ec_ioctl_config_t data;
     ec_slave_config_t *sc;
+    int ret;
 
     sc = malloc(sizeof(ec_slave_config_t));
     if (!sc) {
@@ -172,9 +178,10 @@
     data.vendor_id = vendor_id;
     data.product_code = product_code;
 
-    if (ioctl(master->fd, EC_IOCTL_CREATE_SLAVE_CONFIG, &data) == -1) {
+    ret = ioctl(master->fd, EC_IOCTL_CREATE_SLAVE_CONFIG, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to create slave config: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
         free(sc);
         return 0;
     }
@@ -194,13 +201,16 @@
 
 /****************************************************************************/
 
-int ecrt_master(ec_master_t* master, ec_master_info_t *master_info)
+int ecrt_master(ec_master_t *master, ec_master_info_t *master_info)
 {
     ec_ioctl_master_t data;
-
-    if (ioctl(master->fd, EC_IOCTL_MASTER, &data) < 0) {
-        fprintf(stderr, "Failed to get master info: %s\n", strerror(errno));
-        return -1;
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_MASTER, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to get master info: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     master_info->slave_count = data.slave_count;
@@ -216,13 +226,15 @@
         ec_slave_info_t *slave_info)
 {
     ec_ioctl_slave_t data;
-    int i;
+    int ret, i;
 
     data.position = slave_position;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE, &data) == -1) {
-        fprintf(stderr, "Failed to get slave info: %s\n", strerror(errno));
-        return -1;
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to get slave info: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     slave_info->position = data.position;
@@ -232,7 +244,7 @@
     slave_info->serial_number = data.serial_number;
     slave_info->alias = data.alias;
     slave_info->current_on_ebus = data.current_on_ebus;
-    for ( i = 0; i < EC_MAX_PORTS; i++ ) {
+    for (i = 0; i < EC_MAX_PORTS; i++) {
         slave_info->ports[i].desc = data.ports[i].desc;
         slave_info->ports[i].link.link_up = data.ports[i].link.link_up;
         slave_info->ports[i].link.loop_closed =
@@ -258,6 +270,7 @@
         uint8_t sync_index, ec_sync_info_t *sync)
 {
     ec_ioctl_slave_sync_t data;
+    int ret;
 
     if (sync_index >= EC_MAX_SYNC_MANAGERS) {
         return -ENOENT;
@@ -267,10 +280,11 @@
     data.slave_position = slave_position;
     data.sync_index = sync_index;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC, &data) == -1) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get sync manager information: %s\n",
-                strerror(errno));
-        return -1; // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     sync->index = sync_index;
@@ -290,6 +304,7 @@
         uint8_t sync_index, uint16_t pos, ec_pdo_info_t *pdo)
 {
     ec_ioctl_slave_sync_pdo_t data;
+    int ret;
 
     if (sync_index >= EC_MAX_SYNC_MANAGERS)
         return -ENOENT;
@@ -299,10 +314,11 @@
     data.sync_index = sync_index;
     data.pdo_pos = pos;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO, &data) == -1) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get pdo information: %s\n",
-                strerror(errno));
-        return -1; // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     pdo->index = data.index;
@@ -319,6 +335,7 @@
         ec_pdo_entry_info_t *entry)
 {
     ec_ioctl_slave_sync_pdo_entry_t data;
+    int ret;
 
     if (sync_index >= EC_MAX_SYNC_MANAGERS)
         return -ENOENT;
@@ -329,10 +346,11 @@
     data.pdo_pos = pdo_pos;
     data.entry_pos = entry_pos;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO_ENTRY, &data) == -1) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SYNC_PDO_ENTRY, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get pdo entry information: %s\n",
-                strerror(errno));
-        return -1; // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     entry->index = data.index;
@@ -349,6 +367,7 @@
         size_t data_size, uint32_t *abort_code)
 {
     ec_ioctl_slave_sdo_download_t download;
+    int ret;
 
     download.slave_position = slave_position;
     download.sdo_index = index;
@@ -357,13 +376,14 @@
     download.data_size = data_size;
     download.data = data;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download) == -1) {
-        if (errno == EIO && abort_code) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) {
             *abort_code = download.abort_code;
         }
         fprintf(stderr, "Failed to execute SDO download: %s\n",
-            strerror(errno));
-        return -1;
+            strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -376,6 +396,7 @@
         size_t data_size, uint32_t *abort_code)
 {
     ec_ioctl_slave_sdo_download_t download;
+    int ret;
 
     download.slave_position = slave_position;
     download.sdo_index = index;
@@ -384,13 +405,14 @@
     download.data_size = data_size;
     download.data = data;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download) == -1) {
-        if (errno == EIO && abort_code) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_DOWNLOAD, &download);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) {
             *abort_code = download.abort_code;
         }
         fprintf(stderr, "Failed to execute SDO download: %s\n",
-            strerror(errno));
-        return -1;
+            strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -403,6 +425,7 @@
         size_t target_size, size_t *result_size, uint32_t *abort_code)
 {
     ec_ioctl_slave_sdo_upload_t upload;
+    int ret;
 
     upload.slave_position = slave_position;
     upload.sdo_index = index;
@@ -410,13 +433,14 @@
     upload.target_size = target_size;
     upload.target = target;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SDO_UPLOAD, &upload) == -1) {
-        if (errno == EIO && abort_code) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SDO_UPLOAD, &upload);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        if (EC_IOCTL_ERRNO(ret) == EIO && abort_code) {
             *abort_code = upload.abort_code;
         }
         fprintf(stderr, "Failed to execute SDO upload: %s\n",
-                strerror(errno));
-        return -1;
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     *result_size = upload.data_size;
@@ -430,6 +454,7 @@
         uint16_t *error_code)
 {
     ec_ioctl_slave_soe_write_t io;
+    int ret;
 
     io.slave_position = slave_position;
     io.drive_no = drive_no;
@@ -437,12 +462,14 @@
     io.data_size = data_size;
     io.data = data;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SOE_WRITE, &io) == -1) {
-        if (errno == EIO && error_code) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SOE_WRITE, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        if (EC_IOCTL_ERRNO(ret) == EIO && error_code) {
             *error_code = io.error_code;
         }
-        fprintf(stderr, "Failed to write IDN: %s\n", strerror(errno));
-        return -1;
+        fprintf(stderr, "Failed to write IDN: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -455,6 +482,7 @@
         size_t *result_size, uint16_t *error_code)
 {
     ec_ioctl_slave_soe_read_t io;
+    int ret;
 
     io.slave_position = slave_position;
     io.drive_no = drive_no;
@@ -462,12 +490,14 @@
     io.mem_size = target_size;
     io.data = target;
 
-    if (ioctl(master->fd, EC_IOCTL_SLAVE_SOE_READ, &io) == -1) {
-        if (errno == EIO && error_code) {
+    ret = ioctl(master->fd, EC_IOCTL_SLAVE_SOE_READ, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        if (EC_IOCTL_ERRNO(ret) == EIO && error_code) {
             *error_code = io.error_code;
         }
-        fprintf(stderr, "Failed to read IDN: %s\n", strerror(errno));
-        return -1;
+        fprintf(stderr, "Failed to read IDN: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     *result_size = io.data_size;
@@ -478,23 +508,35 @@
 
 int ecrt_master_activate(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_ACTIVATE,
-                &master->process_data_size) == -1) {
+    ec_ioctl_master_activate_t io;
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_ACTIVATE, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to activate master: %s\n",
-                strerror(errno));
-        return -1; // FIXME
-    }
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
+    }
+
+    master->process_data_size = io.process_data_size;
 
     if (master->process_data_size) {
+#ifdef USE_RTDM
+        /* memory-mapping was already done in kernel. The user-space addess is
+         * provided in the ioctl data.
+         */
+        master->process_data = io.process_data;
+#else
         master->process_data = mmap(0, master->process_data_size,
                 PROT_READ | PROT_WRITE, MAP_SHARED, master->fd, 0);
         if (master->process_data == MAP_FAILED) {
-            fprintf(stderr, "Failed to map process data: %s",
+            fprintf(stderr, "Failed to map process data: %s\n",
                     strerror(errno));
             master->process_data = NULL;
             master->process_data_size = 0;
-            return -1; // FIXME
+            return -errno;
         }
+#endif
 
         // Access the mapped region to cause the initial page fault
         master->process_data[0] = 0x00;
@@ -507,8 +549,12 @@
 
 void ecrt_master_deactivate(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL) == -1) {
-        fprintf(stderr, "Failed to deactivate master: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_DEACTIVATE, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to deactivate master: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
         return;
     }
 
@@ -520,12 +566,15 @@
 int ecrt_master_set_send_interval(ec_master_t *master,
         size_t send_interval_us)
 {
-    if (ioctl(master->fd, EC_IOCTL_SET_SEND_INTERVAL,
-                &send_interval_us) == -1) {
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SET_SEND_INTERVAL, &send_interval_us);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to set send interval: %s\n",
-                strerror(errno));
-        return -1; // FIXME
-    }
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
+    }
+
     return 0;
 }
 
@@ -533,8 +582,12 @@
 
 void ecrt_master_send(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_SEND, NULL) == -1) {
-        fprintf(stderr, "Failed to send: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SEND, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to send: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -542,8 +595,12 @@
 
 void ecrt_master_receive(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_RECEIVE, NULL) == -1) {
-        fprintf(stderr, "Failed to receive: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_RECEIVE, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to receive: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -551,8 +608,12 @@
 
 void ecrt_master_state(const ec_master_t *master, ec_master_state_t *state)
 {
-    if (ioctl(master->fd, EC_IOCTL_MASTER_STATE, state) == -1) {
-        fprintf(stderr, "Failed to get master state: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_MASTER_STATE, state);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to get master state: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -562,12 +623,16 @@
         ec_master_link_state_t *state)
 {
     ec_ioctl_link_state_t io;
+    int ret;
 
     io.dev_idx = dev_idx;
     io.state = state;
-    if (ioctl(master->fd, EC_IOCTL_MASTER_LINK_STATE, &io) == -1) {
-        fprintf(stderr, "Failed to get link state: %s\n", strerror(errno));
-        return -errno;
+
+    ret = ioctl(master->fd, EC_IOCTL_MASTER_LINK_STATE, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to get link state: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -578,12 +643,14 @@
 void ecrt_master_application_time(ec_master_t *master, uint64_t app_time)
 {
     ec_ioctl_app_time_t data;
+    int ret;
 
     data.app_time = app_time;
 
-    if (ioctl(master->fd, EC_IOCTL_APP_TIME, &data) == -1) {
+    ret = ioctl(master->fd, EC_IOCTL_APP_TIME, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to set application time: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -591,9 +658,12 @@
 
 void ecrt_master_sync_reference_clock(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_SYNC_REF, NULL) == -1) {
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SYNC_REF, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to sync reference clock: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -601,8 +671,12 @@
 
 void ecrt_master_sync_slave_clocks(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_SYNC_SLAVES, NULL) == -1) {
-        fprintf(stderr, "Failed to sync slave clocks: %s\n", strerror(errno));
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SYNC_SLAVES, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to sync slave clocks: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -610,9 +684,12 @@
 
 void ecrt_master_sync_monitor_queue(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL) == -1) {
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SYNC_MON_QUEUE, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to queue sync monitor datagram: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -621,11 +698,13 @@
 uint32_t ecrt_master_sync_monitor_process(ec_master_t *master)
 {
     uint32_t time_diff;
-
-    if (ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff) == -1) {
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_SYNC_MON_PROCESS, &time_diff);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         time_diff = 0xffffffff;
         fprintf(stderr, "Failed to process sync monitor datagram: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 
     return time_diff;
@@ -635,9 +714,13 @@
 
 void ecrt_master_reset(ec_master_t *master)
 {
-    if (ioctl(master->fd, EC_IOCTL_RESET, NULL) == -1) {
-        fprintf(stderr, "Failed to reset master: %s\n", strerror(errno));
-    }
-}
-
-/****************************************************************************/
+    int ret;
+
+    ret = ioctl(master->fd, EC_IOCTL_RESET, NULL);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to reset master: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+}
+
+/****************************************************************************/
--- a/lib/sdo_request.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/sdo_request.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -35,12 +35,10 @@
 /*****************************************************************************/
 
 #include <stdio.h>
-#include <errno.h>
 #include <string.h>
-#include <sys/ioctl.h>
 
+#include "ioctl.h"
 #include "sdo_request.h"
-#include "master/ioctl.h"
 #include "slave_config.h"
 #include "master.h"
 
@@ -60,15 +58,17 @@
 void ecrt_sdo_request_timeout(ec_sdo_request_t *req, uint32_t timeout)
 {
     ec_ioctl_sdo_request_t data;
+    int ret;
 
     data.config_index = req->config->index;
     data.request_index = req->index;
     data.timeout = timeout;
 
-    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_TIMEOUT,
-                &data) == -1)
+    ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_TIMEOUT, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to set SDO request timeout: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
 }
 
 /*****************************************************************************/
@@ -90,14 +90,17 @@
 ec_request_state_t ecrt_sdo_request_state(ec_sdo_request_t *req)
 {
     ec_ioctl_sdo_request_t data;
+    int ret;
 
     data.config_index = req->config->index;
     data.request_index = req->index;
 
-    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_STATE,
-                &data) == -1)
+    ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_STATE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get SDO request state: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return EC_REQUEST_ERROR;
+    }
 
     if (data.size) { // new data waiting to be copied
         if (req->mem_size < data.size) {
@@ -108,9 +111,11 @@
 
         data.data = req->data;
 
-        if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_DATA,
-                    &data) == -1) {
-            fprintf(stderr, "Failed to get SDO data: %s\n", strerror(errno));
+        ret = ioctl(req->config->master->fd,
+                EC_IOCTL_SDO_REQUEST_DATA, &data);
+        if (EC_IOCTL_IS_ERROR(ret)) {
+            fprintf(stderr, "Failed to get SDO data: %s\n",
+                    strerror(EC_IOCTL_ERRNO(ret)));
             return EC_REQUEST_ERROR;
         }
         req->data_size = data.size;
@@ -124,14 +129,16 @@
 void ecrt_sdo_request_read(ec_sdo_request_t *req)
 {
     ec_ioctl_sdo_request_t data;
+    int ret;
 
     data.config_index = req->config->index;
     data.request_index = req->index;
 
-    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_READ,
-                &data) == -1)
+    ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_READ, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to command an SDO read operation : %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
 }
 
 /*****************************************************************************/
@@ -139,16 +146,18 @@
 void ecrt_sdo_request_write(ec_sdo_request_t *req)
 {
     ec_ioctl_sdo_request_t data;
+    int ret;
 
     data.config_index = req->config->index;
     data.request_index = req->index;
     data.data = req->data;
     data.size = req->data_size;
 
-    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_WRITE,
-                &data) == -1)
+    ret = ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_WRITE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to command an SDO write operation : %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
 }
 
 /*****************************************************************************/
--- a/lib/slave_config.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/slave_config.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -29,17 +29,16 @@
  *****************************************************************************/
 
 #include <stdlib.h>
-#include <sys/ioctl.h>
 #include <stdio.h>
-#include <errno.h>
 #include <string.h>
-
+#include <errno.h> /* ENOENT */
+
+#include "ioctl.h"
 #include "slave_config.h"
 #include "domain.h"
 #include "sdo_request.h"
 #include "voe_handler.h"
 #include "master.h"
-#include "master/ioctl.h"
 
 /*****************************************************************************/
 
@@ -70,6 +69,7 @@
         ec_direction_t dir, ec_watchdog_mode_t watchdog_mode)
 {
     ec_ioctl_config_t data;
+    int ret;
 
     if (sync_index >= EC_MAX_SYNC_MANAGERS)
         return -ENOENT;
@@ -80,10 +80,11 @@
     data.syncs[sync_index].watchdog_mode = watchdog_mode;
     data.syncs[sync_index].config_this = 1;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_SYNC, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_SYNC, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to config sync manager: %s\n",
-                strerror(errno));
-        return -1; // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -95,15 +96,17 @@
         uint16_t divider, uint16_t intervals)
 {
     ec_ioctl_config_t data;
+    int ret;
 
     memset(&data, 0x00, sizeof(ec_ioctl_config_t));
     data.config_index = sc->index;
     data.watchdog_divider = divider;
     data.watchdog_intervals = intervals;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_WATCHDOG, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_WATCHDOG, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to config watchdog: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -113,15 +116,17 @@
         uint8_t sync_index, uint16_t pdo_index)
 {
     ec_ioctl_config_pdo_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.sync_index = sync_index;
     data.index = pdo_index;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_ADD_PDO, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_ADD_PDO, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to add PDO: %s\n",
-                strerror(errno));
-        return -1;  // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -133,13 +138,15 @@
         uint8_t sync_index)
 {
     ec_ioctl_config_pdo_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.sync_index = sync_index;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_PDOS, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_PDOS, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to clear PDOs: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -150,6 +157,7 @@
         uint8_t entry_bit_length)
 {
     ec_ioctl_add_pdo_entry_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.pdo_index = pdo_index;
@@ -157,10 +165,11 @@
     data.entry_subindex = entry_subindex;
     data.entry_bit_length = entry_bit_length;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_ADD_ENTRY, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_ADD_ENTRY, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to add PDO entry: %s\n",
-                strerror(errno));
-        return -1;  // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -172,13 +181,15 @@
         uint16_t pdo_index)
 {
     ec_ioctl_config_pdo_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.index = pdo_index;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_ENTRIES, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_CLEAR_ENTRIES, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to clear PDO entries: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -264,10 +275,10 @@
     data.domain_index = domain->index;
 
     ret = ioctl(sc->master->fd, EC_IOCTL_SC_REG_PDO_ENTRY, &data);
-    if (ret == -1) {
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to register PDO entry: %s\n",
-                strerror(errno));
-        return -2; // FIXME
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     if (bit_position) {
@@ -277,7 +288,7 @@
             fprintf(stderr, "PDO entry 0x%04X:%02X does not byte-align "
                     "in config %u:%u.\n", index, subindex,
                     sc->alias, sc->position);
-            return -3; // FIXME
+            return -EFAULT;
         }
     }
 
@@ -291,6 +302,7 @@
         uint32_t sync1_cycle_time, uint32_t sync1_shift_time)
 {
     ec_ioctl_config_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.dc_assign_activate = assign_activate;
@@ -299,8 +311,10 @@
     data.dc_sync[1].cycle_time = sync1_cycle_time;
     data.dc_sync[1].shift_time = sync1_shift_time;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_DC, &data) == -1) {
-        fprintf(stderr, "Failed to set assign_activate word.\n");
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_DC, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to set assign_activate word: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -310,6 +324,7 @@
         uint8_t subindex, const uint8_t *sdo_data, size_t size)
 {
     ec_ioctl_sc_sdo_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.index = index;
@@ -318,9 +333,11 @@
     data.size = size;
     data.complete_access = 0;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) {
-        fprintf(stderr, "Failed to configure SDO.\n");
-        return -1; // FIXME
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to configure SDO: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -332,6 +349,7 @@
         const uint8_t *sdo_data, size_t size)
 {
     ec_ioctl_sc_sdo_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.index = index;
@@ -340,9 +358,11 @@
     data.size = size;
     data.complete_access = 1;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data) == -1) {
-        fprintf(stderr, "Failed to configure SDO.\n");
-        return -1; // FIXME
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to configure SDO: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
     }
 
     return 0;
@@ -404,6 +424,7 @@
 {
     ec_ioctl_sdo_request_t data;
     ec_sdo_request_t *req;
+    int ret;
 
     req = malloc(sizeof(ec_sdo_request_t));
     if (!req) {
@@ -428,9 +449,10 @@
     data.sdo_subindex = subindex;
     data.size = size;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO_REQUEST, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_SDO_REQUEST, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to create SDO request: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
         ec_sdo_request_clear(req);
         free(req);
         return NULL;
@@ -472,6 +494,7 @@
 {
     ec_ioctl_voe_t data;
     ec_voe_handler_t *voe;
+    int ret;
 
     voe = malloc(sizeof(ec_voe_handler_t));
     if (!voe) {
@@ -494,9 +517,10 @@
     data.config_index = sc->index;
     data.size = size;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_VOE, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_VOE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to create VoE handler: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
         ec_voe_handler_clear(voe);
         free(voe);
         return NULL;
@@ -519,13 +543,15 @@
         ec_slave_config_state_t *state)
 {
     ec_ioctl_sc_state_t data;
+    int ret;
 
     data.config_index = sc->index;
     data.state = state;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_STATE, &data) == -1) {
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_STATE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get slave configuration state: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -535,6 +561,7 @@
         uint16_t idn, ec_al_state_t al_state, const uint8_t *data, size_t size)
 {
     ec_ioctl_sc_idn_t io;
+    int ret;
 
     io.config_index = sc->index;
     io.drive_no = drive_no;
@@ -543,12 +570,14 @@
     io.data = data;
     io.size = size;
 
-    if (ioctl(sc->master->fd, EC_IOCTL_SC_IDN, &io) == -1) {
-        fprintf(stderr, "Failed to configure IDN.\n");
-        return -1; // FIXME
-    }
-
-    return 0;
-}
-
-/*****************************************************************************/
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_IDN, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to configure IDN: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return -EC_IOCTL_ERRNO(ret);
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
--- a/lib/voe_handler.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/lib/voe_handler.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2009  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT master userspace library.
  *
@@ -35,15 +35,13 @@
 /*****************************************************************************/
 
 #include <stdlib.h>
-#include <sys/ioctl.h>
 #include <stdio.h>
-#include <errno.h>
 #include <string.h>
 
+#include "ioctl.h"
 #include "voe_handler.h"
 #include "slave_config.h"
 #include "master.h"
-#include "master/ioctl.h"
 
 /*****************************************************************************/
 
@@ -60,16 +58,17 @@
         uint16_t vendor_type)
 {
     ec_ioctl_voe_t data;
+    int ret;
 
     data.config_index = voe->config->index;
     data.voe_index = voe->index;
     data.vendor_id = &vendor_id;
     data.vendor_type = &vendor_type;
 
-    if (ioctl(voe->config->master->fd,
-                EC_IOCTL_VOE_SEND_HEADER, &data) == -1) {
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_SEND_HEADER, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to set VoE send header: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -79,16 +78,17 @@
         uint32_t *vendor_id, uint16_t *vendor_type)
 {
     ec_ioctl_voe_t data;
+    int ret;
 
     data.config_index = voe->config->index;
     data.voe_index = voe->index;
     data.vendor_id = vendor_id;
     data.vendor_type = vendor_type;
 
-    if (ioctl(voe->config->master->fd,
-                EC_IOCTL_VOE_REC_HEADER, &data) == -1) {
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_REC_HEADER, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to get received VoE header: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -111,13 +111,15 @@
 void ecrt_voe_handler_read(ec_voe_handler_t *voe)
 {
     ec_ioctl_voe_t data;
-
-    data.config_index = voe->config->index;
-    data.voe_index = voe->index;
-
-    if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ, &data) == -1) {
+    int ret;
+
+    data.config_index = voe->config->index;
+    data.voe_index = voe->index;
+
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to initiate VoE reading: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -126,14 +128,15 @@
 void ecrt_voe_handler_read_nosync(ec_voe_handler_t *voe)
 {
     ec_ioctl_voe_t data;
-
-    data.config_index = voe->config->index;
-    data.voe_index = voe->index;
-
-    if (ioctl(voe->config->master->fd,
-                EC_IOCTL_VOE_READ_NOSYNC, &data) == -1) {
+    int ret;
+
+    data.config_index = voe->config->index;
+    data.voe_index = voe->index;
+
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_READ_NOSYNC, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to initiate VoE reading: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -142,15 +145,17 @@
 void ecrt_voe_handler_write(ec_voe_handler_t *voe, size_t size)
 {
     ec_ioctl_voe_t data;
+    int ret;
 
     data.config_index = voe->config->index;
     data.voe_index = voe->index;
     data.size = size;
     data.data = voe->data;
 
-    if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_WRITE, &data) == -1) {
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_WRITE, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to initiate VoE writing: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
     }
 }
 
@@ -159,13 +164,15 @@
 ec_request_state_t ecrt_voe_handler_execute(ec_voe_handler_t *voe)
 {
     ec_ioctl_voe_t data;
-
-    data.config_index = voe->config->index;
-    data.voe_index = voe->index;
-
-    if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_EXEC, &data) == -1) {
+    int ret;
+
+    data.config_index = voe->config->index;
+    data.voe_index = voe->index;
+
+    ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_EXEC, &data);
+    if (EC_IOCTL_IS_ERROR(ret)) {
         fprintf(stderr, "Failed to execute VoE handler: %s\n",
-                strerror(errno));
+                strerror(EC_IOCTL_ERRNO(ret)));
         return EC_REQUEST_ERROR;
     }
 
@@ -178,8 +185,10 @@
 
         data.data = voe->data;
 
-        if (ioctl(voe->config->master->fd, EC_IOCTL_VOE_DATA, &data) == -1) {
-            fprintf(stderr, "Failed to get VoE data: %s\n", strerror(errno));
+        ret = ioctl(voe->config->master->fd, EC_IOCTL_VOE_DATA, &data);
+        if (EC_IOCTL_IS_ERROR(ret)) {
+            fprintf(stderr, "Failed to get VoE data: %s\n",
+                    strerror(EC_IOCTL_ERRNO(ret)));
             return EC_REQUEST_ERROR;
         }
         voe->data_size = data.size;
--- a/master/Kbuild.in	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/Kbuild.in	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
 #
 #  $Id$
 #
-#  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+#  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
 #
 #  This file is part of the IgH EtherCAT Master.
 #
@@ -52,6 +52,7 @@
 	fsm_slave_config.o \
 	fsm_slave_scan.o \
 	fsm_soe.o \
+	ioctl.o \
 	mailbox.o \
 	master.o \
 	module.o \
@@ -70,12 +71,30 @@
 	voe_handler.o
 
 ifeq (@ENABLE_EOE@,1)
-	ec_master-objs += ethernet.o
+ec_master-objs += ethernet.o
 endif
+
 ifeq (@ENABLE_DEBUG_IF@,1)
-	ec_master-objs += debug.o
+ec_master-objs += debug.o
 endif
 
+ifeq (@ENABLE_RTDM@,1)
+
+ec_master-objs += rtdm.o
+
+ifeq (@ENABLE_XENOMAI@, 1)
+CFLAGS_rtdm.o := -I@XENOMAI_DIR@/include
+endif
+
+ifeq (@ENABLE_RTAI@, 1)
+CFLAGS_rtdm.o := -I@RTAI_DIR@/include
+endif
+
+ec_master-objs += rtdm-ioctl.o
+CFLAGS_rtdm-ioctl.o := -DEC_IOCTL_RTDM
+
+endif # ENABLE_RTDM
+
 REV := $(shell if test -s $(src)/../revision; then \
 		cat $(src)/../revision; \
 	else \
--- a/master/Makefile.am	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/Makefile.am	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
 #
 #  $Id$
 #
-#  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+#  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
 #
 #  This file is part of the IgH EtherCAT Master.
 #
@@ -32,7 +32,7 @@
 	cdev.c cdev.h \
 	datagram.c datagram.h \
 	datagram_pair.c datagram_pair.h \
-	debug.c	debug.h \
+	debug.c debug.h \
 	device.c device.h \
 	domain.c domain.h \
 	doxygen.c \
@@ -52,13 +52,15 @@
 	fsm_slave_scan.c fsm_slave_scan.h \
 	fsm_soe.c fsm_soe.h \
 	globals.h \
-	ioctl.h \
+	ioctl.c ioctl.h \
 	mailbox.c mailbox.h \
 	master.c master.h \
 	module.c \
 	pdo.c pdo.h \
 	pdo_entry.c pdo_entry.h \
 	pdo_list.c pdo_list.h \
+	rtdm-ioctl.c \
+	rtdm.c rtdm.h \
 	sdo.c sdo.h \
 	sdo_entry.c sdo_entry.h \
 	sdo_request.c sdo_request.h \
--- a/master/cdev.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/cdev.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT Master.
  *
@@ -45,15 +45,9 @@
 #include "ethernet.h"
 #include "ioctl.h"
 
-/** Set to 1 to enable ioctl() command debugging.
- */
-#define DEBUG_IOCTL 0
-
-/** Set to 1 to enable ioctl() latency warnings.
- *
- * Requires CPU timestamp counter!
- */
-#define DEBUG_LATENCY 0
+/** Set to 1 to enable device operations debugging.
+ */
+#define DEBUG 0
 
 /*****************************************************************************/
 
@@ -102,9 +96,7 @@
  */
 typedef struct {
     ec_cdev_t *cdev; /**< Character device. */
-    unsigned int requested; /**< Master wac 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 ctx; /**< Context. */
 } ec_cdev_priv_t;
 
 /*****************************************************************************/
@@ -144,3275 +136,6 @@
     cdev_del(&cdev->cdev);
 }
 
-/*****************************************************************************/
-
-/** Copies a string to an ioctl structure.
- */
-void ec_cdev_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.
- */
-int ec_cdev_ioctl_module(
-        unsigned long 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.
- */
-int ec_cdev_ioctl_master(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< Userspace address to store the results. */
-        )
-{
-    ec_ioctl_master_t data;
-    unsigned int i, j;
-
-    if (down_interruptible(&master->master_sem))
-        return -EINTR;
-
-    data.slave_count = master->slave_count;
-    data.config_count = ec_master_config_count(master);
-    data.domain_count = ec_master_domain_count(master);
-#ifdef EC_EOE
-    data.eoe_handler_count = ec_master_eoe_handler_count(master);
-#endif
-    data.phase = (uint8_t) master->phase;
-    data.active = (uint8_t) master->active;
-    data.scan_busy = master->scan_busy;
-
-    up(&master->master_sem);
-
-    if (down_interruptible(&master->device_sem))
-        return -EINTR;
-
-    for (i = 0; i < EC_NUM_DEVICES; i++) {
-        ec_device_t *device = &master->devices[i];
-
-        if (device->dev) {
-            memcpy(data.devices[i].address,
-                    device->dev->dev_addr, ETH_ALEN);
-        } else {
-            memcpy(data.devices[i].address, master->macs[i], ETH_ALEN);
-        }
-        data.devices[i].attached = device->dev ? 1 : 0;
-        data.devices[i].link_state = device->link_state ? 1 : 0;
-        data.devices[i].tx_count = device->tx_count;
-        data.devices[i].rx_count = device->rx_count;
-        data.devices[i].tx_bytes = device->tx_bytes;
-        data.devices[i].rx_bytes = device->rx_bytes;
-        data.devices[i].tx_errors = device->tx_errors;
-        for (j = 0; j < EC_RATE_COUNT; j++) {
-            data.devices[i].tx_frame_rates[j] =
-                device->tx_frame_rates[j];
-            data.devices[i].rx_frame_rates[j] =
-                device->rx_frame_rates[j];
-            data.devices[i].tx_byte_rates[j] =
-                device->tx_byte_rates[j];
-            data.devices[i].rx_byte_rates[j] =
-                device->rx_byte_rates[j];
-        }
-    }
-
-    data.tx_count = master->device_stats.tx_count;
-    data.rx_count = master->device_stats.rx_count;
-    data.tx_bytes = master->device_stats.tx_bytes;
-    data.rx_bytes = master->device_stats.rx_bytes;
-    for (i = 0; i < EC_RATE_COUNT; i++) {
-        data.tx_frame_rates[i] =
-            master->device_stats.tx_frame_rates[i];
-        data.rx_frame_rates[i] =
-            master->device_stats.rx_frame_rates[i];
-        data.tx_byte_rates[i] =
-            master->device_stats.tx_byte_rates[i];
-        data.rx_byte_rates[i] =
-            master->device_stats.rx_byte_rates[i];
-        data.loss_rates[i] =
-            master->device_stats.loss_rates[i];
-    }
-
-    up(&master->device_sem);
-
-    data.app_time = master->app_time;
-    data.ref_clock =
-        master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
-
-    if (copy_to_user((void __user *) arg, &data, sizeof(data)))
-        return -EFAULT;
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Get slave information.
- */
-int ec_cdev_ioctl_slave(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_strcpy(data.group, slave->sii.group);
-    ec_cdev_strcpy(data.image, slave->sii.image);
-    ec_cdev_strcpy(data.order, slave->sii.order);
-    ec_cdev_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.
- */
-int ec_cdev_ioctl_slave_sync(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_slave_sync_pdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_slave_sync_pdo_entry(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_domain(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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 = 0; dev_idx < EC_NUM_DEVICES; 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.
- */
-int ec_cdev_ioctl_domain_fmmu(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_domain_data(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_master_debug(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    return ec_master_debug_level(master, (unsigned int) arg);
-}
-
-/*****************************************************************************/
-
-/** Issue a bus scan.
- */
-int ec_cdev_ioctl_master_rescan(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    master->fsm.rescan_required = 1;
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Set slave state.
- */
-int ec_cdev_ioctl_slave_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_slave_sdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_slave_sdo_entry(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_slave_sdo_upload(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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 %u 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 0;
-}
-
-/*****************************************************************************/
-
-/** Download SDO.
- */
-int ec_cdev_ioctl_slave_sdo_download(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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 %u 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.
- */
-int ec_cdev_ioctl_slave_sii_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_slave_sii_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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))
-        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->sii_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->sii_queue, request.state != EC_INT_REQUEST_BUSY);
-
-    kfree(words);
-
-    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
-}
-
-/*****************************************************************************/
-
-/** Read a slave's registers.
- */
-int ec_cdev_ioctl_slave_reg_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    ec_ioctl_slave_reg_t data;
-    ec_slave_t *slave;
-    uint8_t *contents;
-    ec_reg_request_t request;
-
-    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
-        return -EFAULT;
-    }
-
-    if (!data.length)
-        return 0;
-
-    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
-        EC_MASTER_ERR(master, "Failed to allocate %u bytes"
-                " for register data.\n", data.length);
-        return -ENOMEM;
-    }
-
-    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;
-    }
-
-    // init register request
-    INIT_LIST_HEAD(&request.list);
-    request.slave = slave;
-    request.dir = EC_DIR_INPUT;
-    request.data = contents;
-    request.offset = data.offset;
-    request.length = data.length;
-    request.state = EC_INT_REQUEST_QUEUED;
-
-    // schedule request.
-    list_add_tail(&request.list, &master->reg_requests);
-
-    up(&master->master_sem);
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(master->reg_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(contents);
-            return -EINTR;
-        }
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY);
-
-    if (request.state == EC_INT_REQUEST_SUCCESS) {
-        if (copy_to_user((void __user *) data.data, contents, data.length))
-            return -EFAULT;
-    }
-    kfree(contents);
-
-    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
-}
-
-/*****************************************************************************/
-
-/** Write a slave's registers.
- */
-int ec_cdev_ioctl_slave_reg_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    ec_ioctl_slave_reg_t data;
-    ec_slave_t *slave;
-    uint8_t *contents;
-    ec_reg_request_t request;
-
-    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
-        return -EFAULT;
-    }
-
-    if (!data.length)
-        return 0;
-
-    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
-        EC_MASTER_ERR(master, "Failed to allocate %u bytes"
-                " for register data.\n", data.length);
-        return -ENOMEM;
-    }
-
-    if (copy_from_user(contents, (void __user *) data.data, data.length)) {
-        kfree(contents);
-        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);
-        kfree(contents);
-        return -EINVAL;
-    }
-
-    // init register request
-    INIT_LIST_HEAD(&request.list);
-    request.slave = slave;
-    request.dir = EC_DIR_OUTPUT;
-    request.data = contents;
-    request.offset = data.offset;
-    request.length = data.length;
-    request.state = EC_INT_REQUEST_QUEUED;
-
-    // schedule request.
-    list_add_tail(&request.list, &master->reg_requests);
-
-    up(&master->master_sem);
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(master->reg_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(contents);
-            return -EINTR;
-        }
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY);
-
-    kfree(contents);
-
-    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
-}
-
-/*****************************************************************************/
-
-/** Get slave configuration information.
- */
-int ec_cdev_ioctl_config(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_config_pdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_config_pdo_entry(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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_cdev_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.
- */
-int ec_cdev_ioctl_config_sdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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));
-
-    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.
- */
-int ec_cdev_ioctl_config_idn(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_eoe_handler(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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.
- */
-int ec_cdev_ioctl_request(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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 {
-        priv->requested = 1;
-    }
-
-    return ret;
-}
-
-/*****************************************************************************/
-
-/** Create a domain.
- */
-int ec_cdev_ioctl_create_domain(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_domain_t *domain;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_create_slave_config(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_t data;
-    ec_slave_config_t *sc, *entry;
-
-    if (unlikely(!priv->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;
-}
-
-/*****************************************************************************/
-
-/** Activates the master.
- */
-int ec_cdev_ioctl_activate(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_domain_t *domain;
-    off_t offset;
-    int ret;
-
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    /* Get the sum of the domains' process data sizes. */
-
-    priv->process_data_size = 0;
-
-    if (down_interruptible(&master->master_sem))
-        return -EINTR;
-
-    list_for_each_entry(domain, &master->domains, list) {
-        priv->process_data_size += ecrt_domain_size(domain);
-    }
-
-    up(&master->master_sem);
-
-    if (priv->process_data_size) {
-        priv->process_data = vmalloc(priv->process_data_size);
-        if (!priv->process_data) {
-            priv->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, priv->process_data + offset);
-            offset += ecrt_domain_size(domain);
-        }
-    }
-
-    ecrt_master_callbacks(master, ec_master_internal_send_cb,
-            ec_master_internal_receive_cb, master);
-
-    ret = ecrt_master_activate(master);
-    if (ret < 0)
-        return ret;
-
-    if (copy_to_user((void __user *) arg,
-                &priv->process_data_size, sizeof(size_t)))
-        return -EFAULT;
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Deactivates the master.
- */
-int ec_cdev_ioctl_deactivate(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    ecrt_master_deactivate(master);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Set max. number of databytes in a cycle
- */
-int ec_cdev_ioctl_set_send_interval(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    size_t send_interval;
-
-    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.
- */
-int ec_cdev_ioctl_send(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    ecrt_master_send(master);
-    up(&master->io_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Receive frames.
- */
-int ec_cdev_ioctl_receive(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    ecrt_master_receive(master);
-    up(&master->io_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Get the master state.
- */
-int ec_cdev_ioctl_master_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_master_state_t data;
-
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    ecrt_master_state(master, &data);
-
-    if (copy_to_user((void __user *) arg, &data, sizeof(data)))
-        return -EFAULT;
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Get the master state.
- */
-int ec_cdev_ioctl_master_link_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_link_state_t ioctl;
-    ec_master_link_state_t state;
-    int ret;
-
-    if (unlikely(!priv->requested)) {
-        return -EPERM;
-    }
-
-    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;
-}
-
-/*****************************************************************************/
-
-/** Get the master state.
- */
-int ec_cdev_ioctl_app_time(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_app_time_t data;
-
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
-        return -EFAULT;
-    }
-
-    ecrt_master_application_time(master, data.app_time);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Sync the reference clock.
- */
-int ec_cdev_ioctl_sync_ref(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    ecrt_master_sync_reference_clock(master);
-    up(&master->io_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Sync the slave clocks.
- */
-int ec_cdev_ioctl_sync_slaves(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    ecrt_master_sync_slave_clocks(master);
-    up(&master->io_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Queue the sync monitoring datagram.
- */
-int ec_cdev_ioctl_sync_mon_queue(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    ecrt_master_sync_monitor_queue(master);
-    up(&master->io_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Processes the sync monitoring datagram.
- */
-int ec_cdev_ioctl_sync_mon_process(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    uint32_t time_diff;
-
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->io_sem);
-    time_diff = ecrt_master_sync_monitor_process(master);
-    up(&master->io_sem);
-
-    if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
-        return -EFAULT;
-
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Reset configuration.
- */
-int ec_cdev_ioctl_reset(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    down(&master->master_sem);
-    ecrt_master_reset(master);
-    up(&master->master_sem);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Configure a sync manager.
- */
-int ec_cdev_ioctl_sc_sync(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_t data;
-    ec_slave_config_t *sc;
-    unsigned int i;
-    int ret = 0;
-
-    if (unlikely(!priv->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) {
-            if (ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir,
-                        data.syncs[i].watchdog_mode)) {
-                ret = -EINVAL;
-                goto out_up;
-            }
-        }
-    }
-
-out_up:
-    up(&master->master_sem);
-out_return:
-    return ret;
-}
-
-/*****************************************************************************/
-
-/** Configure a slave's watchdogs.
- */
-int ec_cdev_ioctl_sc_watchdog(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_t data;
-    ec_slave_config_t *sc;
-    int ret = 0;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_sc_add_pdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_pdo_t data;
-    ec_slave_config_t *sc;
-
-    if (unlikely(!priv->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); /** \fixme sc could be invalidated */
-
-    return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index);
-}
-
-/*****************************************************************************/
-
-/** Clears the PDO assignment.
- */
-int ec_cdev_ioctl_sc_clear_pdos(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_pdo_t data;
-    ec_slave_config_t *sc;
-
-    if (unlikely(!priv->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); /** \fixme sc could be invalidated */
-
-    ecrt_slave_config_pdo_assign_clear(sc, data.sync_index);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Add an entry to a PDO's mapping.
- */
-int ec_cdev_ioctl_sc_add_entry(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_add_pdo_entry_t data;
-    ec_slave_config_t *sc;
-
-    if (unlikely(!priv->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); /** \fixme 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.
- */
-int ec_cdev_ioctl_sc_clear_entries(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_pdo_t data;
-    ec_slave_config_t *sc;
-
-    if (unlikely(!priv->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); /** \fixme sc could be invalidated */
-
-    ecrt_slave_config_pdo_mapping_clear(sc, data.index);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Registers a PDO entry.
- */
-int ec_cdev_ioctl_sc_reg_pdo_entry(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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); /** \fixme 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;
-}
-
-/*****************************************************************************/
-
-/** Sets the DC AssignActivate word and the sync signal times.
- */
-int ec_cdev_ioctl_sc_dc(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_config_t data;
-    ec_slave_config_t *sc;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_sc_sdo(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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); /** \fixme 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;
-}
-
-/*****************************************************************************/
-
-/** Create an SDO request.
- */
-int ec_cdev_ioctl_sc_create_sdo_request(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_sdo_request_t data;
-    ec_slave_config_t *sc;
-    ec_sdo_request_t *req;
-
-    if (unlikely(!priv->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); /** \fixme 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 VoE handler.
- */
-int ec_cdev_ioctl_sc_create_voe_handler(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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); /** \fixme 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.
- */
-int ec_cdev_ioctl_sc_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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.
- */
-int ec_cdev_ioctl_sc_idn(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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); /** \fixme 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 offset in the total process data.
- */
-int ec_cdev_ioctl_domain_offset(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    int offset = 0;
-    const ec_domain_t *domain;
-
-    if (unlikely(!priv->requested))
-        return -EPERM;
-
-    if (down_interruptible(&master->master_sem)) {
-        return -EINTR;
-    }
-
-    list_for_each_entry(domain, &master->domains, list) {
-        if (domain->index == arg) {
-            up(&master->master_sem);
-            return offset;
-        }
-        offset += ecrt_domain_size(domain);
-    }
-
-    up(&master->master_sem);
-    return -ENOENT;
-}
-
-/*****************************************************************************/
-
-/** Process the domain.
- */
-int ec_cdev_ioctl_domain_process(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_domain_t *domain;
-
-    if (unlikely(!priv->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, arg))) {
-        return -ENOENT;
-    }
-
-    ecrt_domain_process(domain);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Queue the domain.
- */
-int ec_cdev_ioctl_domain_queue(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_domain_t *domain;
-
-    if (unlikely(!priv->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, arg))) {
-        return -ENOENT;
-    }
-
-    ecrt_domain_queue(domain);
-    return 0;
-}
-
-/*****************************************************************************/
-
-/** Get the domain state.
- */
-int ec_cdev_ioctl_domain_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_domain_state_t data;
-    const ec_domain_t *domain;
-    ec_domain_state_t state;
-
-    if (unlikely(!priv->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 timeout.
- */
-int ec_cdev_ioctl_sdo_request_timeout(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_sdo_request_t data;
-    ec_slave_config_t *sc;
-    ec_sdo_request_t *req;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_sdo_request_state(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_sdo_request_t data;
-    ec_slave_config_t *sc;
-    ec_sdo_request_t *req;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_sdo_request_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_sdo_request_t data;
-    ec_slave_config_t *sc;
-    ec_sdo_request_t *req;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_sdo_request_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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.
- */
-int ec_cdev_ioctl_sdo_request_data(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_sdo_request_t data;
-    ec_slave_config_t *sc;
-    ec_sdo_request_t *req;
-
-    if (unlikely(!priv->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;
-}
-
-/*****************************************************************************/
-
-/** Sets the VoE send header.
- */
-int ec_cdev_ioctl_voe_send_header(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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.
- */
-int ec_cdev_ioctl_voe_rec_header(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< 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(!priv->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.
- */
-int ec_cdev_ioctl_voe_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_voe_read_nosync(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_voe_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_voe_exec(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_voe_data(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg, /**< ioctl() argument. */
-        ec_cdev_priv_t *priv /**< Private data structure of file handle. */
-        )
-{
-    ec_ioctl_voe_t data;
-    ec_slave_config_t *sc;
-    ec_voe_handler_t *voe;
-
-    if (unlikely(!priv->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.
- */
-int ec_cdev_ioctl_slave_foe_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    ec_ioctl_slave_foe_t data;
-    ec_master_foe_request_t request;
-    int retval;
-
-    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
-        return -EFAULT;
-    }
-
-    ec_foe_request_init(&request.req, data.file_name);
-    ec_foe_request_read(&request.req);
-    ec_foe_request_alloc(&request.req, 10000); // FIXME
-
-    if (down_interruptible(&master->master_sem)) {
-        ec_foe_request_clear(&request.req);
-        return -EINTR;
-    }
-
-    if (!(request.slave = ec_master_find_slave(
-                    master, 0, data.slave_position))) {
-        up(&master->master_sem);
-        ec_foe_request_clear(&request.req);
-        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
-                data.slave_position);
-        return -EINVAL;
-    }
-
-    // schedule request.
-    list_add_tail(&request.list, &request.slave->foe_requests);
-
-    up(&master->master_sem);
-
-    EC_SLAVE_DBG(request.slave, 1, "Scheduled FoE read request.\n");
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(request.slave->foe_queue,
-                request.req.state != EC_INT_REQUEST_QUEUED)) {
-        // interrupted by signal
-        down(&master->master_sem);
-        if (request.req.state == EC_INT_REQUEST_QUEUED) {
-            list_del(&request.list);
-            up(&master->master_sem);
-            ec_foe_request_clear(&request.req);
-            return -EINTR;
-        }
-        // request already processing: interrupt not possible.
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(request.slave->foe_queue,
-            request.req.state != EC_INT_REQUEST_BUSY);
-
-    data.result = request.req.result;
-    data.error_code = request.req.error_code;
-
-    EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via FoE"
-            " (result = 0x%x).\n", request.req.data_size, request.req.result);
-
-    if (request.req.state != EC_INT_REQUEST_SUCCESS) {
-        data.data_size = 0;
-        retval = -EIO;
-    } else {
-        if (request.req.data_size > data.buffer_size) {
-            EC_MASTER_ERR(master, "Buffer too small.\n");
-            ec_foe_request_clear(&request.req);
-            return -EOVERFLOW;
-        }
-        data.data_size = request.req.data_size;
-        if (copy_to_user((void __user *) data.buffer,
-                    request.req.buffer, data.data_size)) {
-            ec_foe_request_clear(&request.req);
-            return -EFAULT;
-        }
-        retval = 0;
-    }
-
-    if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
-        retval = -EFAULT;
-    }
-
-    EC_SLAVE_DBG(request.slave, 1, "Finished FoE read request.\n");
-
-    ec_foe_request_clear(&request.req);
-
-    return retval;
-}
-
-/*****************************************************************************/
-
-/** Write a file to a slave via FoE
- */
-int ec_cdev_ioctl_slave_foe_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long arg /**< ioctl() argument. */
-        )
-{
-    ec_ioctl_slave_foe_t data;
-    ec_master_foe_request_t request;
-    int retval;
-
-    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
-        return -EFAULT;
-    }
-
-    INIT_LIST_HEAD(&request.list);
-
-    ec_foe_request_init(&request.req, data.file_name);
-
-    if (ec_foe_request_alloc(&request.req, data.buffer_size)) {
-        ec_foe_request_clear(&request.req);
-        return -ENOMEM;
-    }
-    if (copy_from_user(request.req.buffer,
-                (void __user *) data.buffer, data.buffer_size)) {
-        ec_foe_request_clear(&request.req);
-        return -EFAULT;
-    }
-    request.req.data_size = data.buffer_size;
-    ec_foe_request_write(&request.req);
-
-    if (down_interruptible(&master->master_sem)) {
-        ec_foe_request_clear(&request.req);
-        return -EINTR;
-    }
-
-    if (!(request.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);
-        ec_foe_request_clear(&request.req);
-        return -EINVAL;
-    }
-
-    EC_SLAVE_DBG(request.slave, 1, "Scheduling FoE write request.\n");
-
-    // schedule FoE write request.
-    list_add_tail(&request.list, &request.slave->foe_requests);
-
-    up(&master->master_sem);
-
-    // wait for processing through FSM
-    if (wait_event_interruptible(request.slave->foe_queue,
-                request.req.state != EC_INT_REQUEST_QUEUED)) {
-        // interrupted by signal
-        down(&master->master_sem);
-        if (request.req.state == EC_INT_REQUEST_QUEUED) {
-            // abort request
-            list_del(&request.list);
-            up(&master->master_sem);
-            ec_foe_request_clear(&request.req);
-            return -EINTR;
-        }
-        up(&master->master_sem);
-    }
-
-    // wait until master FSM has finished processing
-    wait_event(request.slave->foe_queue,
-            request.req.state != EC_INT_REQUEST_BUSY);
-
-    data.result = request.req.result;
-    data.error_code = request.req.error_code;
-
-    retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
-
-    if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
-        retval = -EFAULT;
-    }
-
-    ec_foe_request_clear(&request.req);
-
-    EC_SLAVE_DBG(request.slave, 1, "Finished FoE write request.\n");
-
-    return retval;
-}
-
-/*****************************************************************************/
-
-/** Read an SoE IDN.
- */
-int ec_cdev_ioctl_slave_soe_read(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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 %u 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.
- */
-int ec_cdev_ioctl_slave_soe_write(
-        ec_master_t *master, /**< EtherCAT master. */
-        unsigned long 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;
-}
-
 /******************************************************************************
  * File operations
  *****************************************************************************/
@@ -3432,13 +155,14 @@
     }
 
     priv->cdev = cdev;
-    priv->requested = 0;
-    priv->process_data = NULL;
-    priv->process_data_size = 0;
+    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_IOCTL
+#if DEBUG
     EC_MASTER_DBG(cdev->master, 0, "File opened.\n");
 #endif
     return 0;
@@ -3453,13 +177,15 @@
     ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
     ec_master_t *master = priv->cdev->master;
 
-    if (priv->requested)
+    if (priv->ctx.requested) {
         ecrt_release_master(master);
-
-    if (priv->process_data)
-        vfree(priv->process_data);
-
-#if DEBUG_IOCTL
+    }
+
+    if (priv->ctx.process_data) {
+        vfree(priv->ctx.process_data);
+    }
+
+#if DEBUG
     EC_MASTER_DBG(master, 0, "File closed.\n");
 #endif
 
@@ -3473,438 +199,15 @@
  */
 long eccdev_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 {
-#if DEBUG_LATENCY
-    cycles_t a = get_cycles(), b;
-    unsigned int t;
-#endif
     ec_cdev_priv_t *priv = (ec_cdev_priv_t *) filp->private_data;
-    ec_master_t *master = priv->cdev->master;
-    int ret;
-
-#if DEBUG_IOCTL
-    EC_MASTER_DBG(master, 0, "ioctl(filp = 0x%p, cmd = 0x%08x (0x%02x),"
-            " arg = 0x%lx)\n", filp, cmd, _IOC_NR(cmd), arg);
-#endif
-
-    switch (cmd) {
-        case EC_IOCTL_MODULE:
-            ret = ec_cdev_ioctl_module(arg);
-            break;
-        case EC_IOCTL_MASTER:
-            ret = ec_cdev_ioctl_master(master, arg);
-            break;
-        case EC_IOCTL_SLAVE:
-            ret = ec_cdev_ioctl_slave(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SYNC:
-            ret = ec_cdev_ioctl_slave_sync(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SYNC_PDO:
-            ret = ec_cdev_ioctl_slave_sync_pdo(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SYNC_PDO_ENTRY:
-            ret = ec_cdev_ioctl_slave_sync_pdo_entry(master, arg);
-            break;
-        case EC_IOCTL_DOMAIN:
-            ret = ec_cdev_ioctl_domain(master, arg);
-            break;
-        case EC_IOCTL_DOMAIN_FMMU:
-            ret = ec_cdev_ioctl_domain_fmmu(master, arg);
-            break;
-        case EC_IOCTL_DOMAIN_DATA:
-            ret = ec_cdev_ioctl_domain_data(master, arg);
-            break;
-        case EC_IOCTL_MASTER_DEBUG:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_master_debug(master, arg);
-            break;
-        case EC_IOCTL_MASTER_RESCAN:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_master_rescan(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_STATE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_state(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SDO:
-            ret = ec_cdev_ioctl_slave_sdo(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SDO_ENTRY:
-            ret = ec_cdev_ioctl_slave_sdo_entry(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SDO_UPLOAD:
-            ret = ec_cdev_ioctl_slave_sdo_upload(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SDO_DOWNLOAD:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_sdo_download(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SII_READ:
-            ret = ec_cdev_ioctl_slave_sii_read(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SII_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_sii_write(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_REG_READ:
-            ret = ec_cdev_ioctl_slave_reg_read(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_REG_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_reg_write(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_FOE_READ:
-            ret = ec_cdev_ioctl_slave_foe_read(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_FOE_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_foe_write(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SOE_READ:
-            ret = ec_cdev_ioctl_slave_soe_read(master, arg);
-            break;
-        case EC_IOCTL_SLAVE_SOE_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_slave_soe_write(master, arg);
-            break;
-        case EC_IOCTL_CONFIG:
-            ret = ec_cdev_ioctl_config(master, arg);
-            break;
-        case EC_IOCTL_CONFIG_PDO:
-            ret = ec_cdev_ioctl_config_pdo(master, arg);
-            break;
-        case EC_IOCTL_CONFIG_PDO_ENTRY:
-            ret = ec_cdev_ioctl_config_pdo_entry(master, arg);
-            break;
-        case EC_IOCTL_CONFIG_SDO:
-            ret = ec_cdev_ioctl_config_sdo(master, arg);
-            break;
-        case EC_IOCTL_CONFIG_IDN:
-            ret = ec_cdev_ioctl_config_idn(master, arg);
-            break;
-#ifdef EC_EOE
-        case EC_IOCTL_EOE_HANDLER:
-            ret = ec_cdev_ioctl_eoe_handler(master, arg);
-            break;
-#endif
-        case EC_IOCTL_REQUEST:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_request(master, arg, priv);
-            break;
-        case EC_IOCTL_CREATE_DOMAIN:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_create_domain(master, arg, priv);
-            break;
-        case EC_IOCTL_CREATE_SLAVE_CONFIG:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_create_slave_config(master, arg, priv);
-            break;
-        case EC_IOCTL_ACTIVATE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_activate(master, arg, priv);
-            break;
-        case EC_IOCTL_DEACTIVATE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_deactivate(master, arg, priv);
-            break;
-        case EC_IOCTL_SEND:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_send(master, arg, priv);
-            break;
-        case EC_IOCTL_RECEIVE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_receive(master, arg, priv);
-            break;
-        case EC_IOCTL_MASTER_STATE:
-            ret = ec_cdev_ioctl_master_state(master, arg, priv);
-            break;
-        case EC_IOCTL_MASTER_LINK_STATE:
-            ret = ec_cdev_ioctl_master_link_state(master, arg, priv);
-            break;
-        case EC_IOCTL_APP_TIME:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_app_time(master, arg, priv);
-            break;
-        case EC_IOCTL_SYNC_REF:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sync_ref(master, arg, priv);
-            break;
-        case EC_IOCTL_SYNC_SLAVES:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sync_slaves(master, arg, priv);
-            break;
-        case EC_IOCTL_SYNC_MON_QUEUE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sync_mon_queue(master, arg, priv);
-            break;
-        case EC_IOCTL_SYNC_MON_PROCESS:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sync_mon_process(master, arg, priv);
-            break;
-        case EC_IOCTL_RESET:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_reset(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_SYNC:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_sync(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_WATCHDOG:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_watchdog(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_ADD_PDO:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_add_pdo(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_CLEAR_PDOS:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_clear_pdos(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_ADD_ENTRY:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_add_entry(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_CLEAR_ENTRIES:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_clear_entries(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_REG_PDO_ENTRY:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_reg_pdo_entry(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_DC:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_dc(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_SDO:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_sdo(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_SDO_REQUEST:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_create_sdo_request(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_VOE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_create_voe_handler(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_STATE:
-            ret = ec_cdev_ioctl_sc_state(master, arg, priv);
-            break;
-        case EC_IOCTL_SC_IDN:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sc_idn(master, arg, priv);
-            break;
-        case EC_IOCTL_DOMAIN_OFFSET:
-            ret = ec_cdev_ioctl_domain_offset(master, arg, priv);
-            break;
-        case EC_IOCTL_DOMAIN_PROCESS:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_domain_process(master, arg, priv);
-            break;
-        case EC_IOCTL_DOMAIN_QUEUE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_domain_queue(master, arg, priv);
-            break;
-        case EC_IOCTL_DOMAIN_STATE:
-            ret = ec_cdev_ioctl_domain_state(master, arg, priv);
-            break;
-        case EC_IOCTL_SDO_REQUEST_TIMEOUT:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sdo_request_timeout(master, arg, priv);
-            break;
-        case EC_IOCTL_SDO_REQUEST_STATE:
-            ret = ec_cdev_ioctl_sdo_request_state(master, arg, priv);
-            break;
-        case EC_IOCTL_SDO_REQUEST_READ:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sdo_request_read(master, arg, priv);
-            break;
-        case EC_IOCTL_SDO_REQUEST_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_sdo_request_write(master, arg, priv);
-            break;
-        case EC_IOCTL_SDO_REQUEST_DATA:
-            ret = ec_cdev_ioctl_sdo_request_data(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_SEND_HEADER:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_voe_send_header(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_REC_HEADER:
-            ret = ec_cdev_ioctl_voe_rec_header(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_READ:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_voe_read(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_READ_NOSYNC:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_voe_read_nosync(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_WRITE:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_voe_write(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_EXEC:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_voe_exec(master, arg, priv);
-            break;
-        case EC_IOCTL_VOE_DATA:
-            ret = ec_cdev_ioctl_voe_data(master, arg, priv);
-            break;
-        case EC_IOCTL_SET_SEND_INTERVAL:
-            if (!(filp->f_mode & FMODE_WRITE)) {
-                ret = -EPERM;
-                break;
-            }
-            ret = ec_cdev_ioctl_set_send_interval(master, arg, priv);
-            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;
+
+#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);
 }
 
 /*****************************************************************************/
@@ -3948,10 +251,10 @@
     ec_cdev_priv_t *priv = (ec_cdev_priv_t *) vma->vm_private_data;
     struct page *page;
 
-    if (offset >= priv->process_data_size)
+    if (offset >= priv->ctx.process_data_size)
         return VM_FAULT_SIGBUS;
 
-    page = vmalloc_to_page(priv->process_data + offset);
+    page = vmalloc_to_page(priv->ctx.process_data + offset);
     if (!page)
         return VM_FAULT_SIGBUS;
 
@@ -3985,10 +288,10 @@
 
     offset = (address - vma->vm_start) + (vma->vm_pgoff << PAGE_SHIFT);
 
-    if (offset >= priv->process_data_size)
+    if (offset >= priv->ctx.process_data_size)
         return NOPAGE_SIGBUS;
 
-    page = vmalloc_to_page(priv->process_data + offset);
+    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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/ioctl.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,3781 @@
+/******************************************************************************
+ *
+ *  $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 "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
+
+/*****************************************************************************/
+
+/** 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.
+ */
+static 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.
+ */
+static int ec_ioctl_master(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< Userspace address to store the results. */
+        )
+{
+    ec_ioctl_master_t data;
+    unsigned int i, j;
+
+    if (down_interruptible(&master->master_sem))
+        return -EINTR;
+
+    data.slave_count = master->slave_count;
+    data.config_count = ec_master_config_count(master);
+    data.domain_count = ec_master_domain_count(master);
+#ifdef EC_EOE
+    data.eoe_handler_count = ec_master_eoe_handler_count(master);
+#endif
+    data.phase = (uint8_t) master->phase;
+    data.active = (uint8_t) master->active;
+    data.scan_busy = master->scan_busy;
+
+    up(&master->master_sem);
+
+    if (down_interruptible(&master->device_sem))
+        return -EINTR;
+
+    for (i = 0; i < EC_NUM_DEVICES; i++) {
+        ec_device_t *device = &master->devices[i];
+
+        if (device->dev) {
+            memcpy(data.devices[i].address,
+                    device->dev->dev_addr, ETH_ALEN);
+        } else {
+            memcpy(data.devices[i].address, master->macs[i], ETH_ALEN);
+        }
+        data.devices[i].attached = device->dev ? 1 : 0;
+        data.devices[i].link_state = device->link_state ? 1 : 0;
+        data.devices[i].tx_count = device->tx_count;
+        data.devices[i].rx_count = device->rx_count;
+        data.devices[i].tx_bytes = device->tx_bytes;
+        data.devices[i].rx_bytes = device->rx_bytes;
+        data.devices[i].tx_errors = device->tx_errors;
+        for (j = 0; j < EC_RATE_COUNT; j++) {
+            data.devices[i].tx_frame_rates[j] =
+                device->tx_frame_rates[j];
+            data.devices[i].rx_frame_rates[j] =
+                device->rx_frame_rates[j];
+            data.devices[i].tx_byte_rates[j] =
+                device->tx_byte_rates[j];
+            data.devices[i].rx_byte_rates[j] =
+                device->rx_byte_rates[j];
+        }
+    }
+
+    data.tx_count = master->device_stats.tx_count;
+    data.rx_count = master->device_stats.rx_count;
+    data.tx_bytes = master->device_stats.tx_bytes;
+    data.rx_bytes = master->device_stats.rx_bytes;
+    for (i = 0; i < EC_RATE_COUNT; i++) {
+        data.tx_frame_rates[i] =
+            master->device_stats.tx_frame_rates[i];
+        data.rx_frame_rates[i] =
+            master->device_stats.rx_frame_rates[i];
+        data.tx_byte_rates[i] =
+            master->device_stats.tx_byte_rates[i];
+        data.rx_byte_rates[i] =
+            master->device_stats.rx_byte_rates[i];
+        data.loss_rates[i] =
+            master->device_stats.loss_rates[i];
+    }
+
+    up(&master->device_sem);
+
+    data.app_time = master->app_time;
+    data.ref_clock =
+        master->dc_ref_clock ? master->dc_ref_clock->ring_position : 0xffff;
+
+    if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+        return -EFAULT;
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Get slave information.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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 = 0; dev_idx < EC_NUM_DEVICES; 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static int ec_ioctl_master_debug(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< ioctl() argument. */
+        )
+{
+    return ec_master_debug_level(master, (unsigned int) arg);
+}
+
+/*****************************************************************************/
+
+/** Issue a bus scan.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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 %u 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 0;
+}
+
+/*****************************************************************************/
+
+/** Download SDO.
+ */
+static 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 %u 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.
+ */
+static 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.
+ */
+static 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))
+        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->sii_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->sii_queue, request.state != EC_INT_REQUEST_BUSY);
+
+    kfree(words);
+
+    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Read a slave's registers.
+ */
+static int ec_ioctl_slave_reg_read(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_reg_t data;
+    ec_slave_t *slave;
+    uint8_t *contents;
+    ec_reg_request_t request;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    if (!data.length)
+        return 0;
+
+    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
+        EC_MASTER_ERR(master, "Failed to allocate %u bytes"
+                " for register data.\n", data.length);
+        return -ENOMEM;
+    }
+
+    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;
+    }
+
+    // init register request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.dir = EC_DIR_INPUT;
+    request.data = contents;
+    request.offset = data.offset;
+    request.length = data.length;
+    request.state = EC_INT_REQUEST_QUEUED;
+
+    // schedule request.
+    list_add_tail(&request.list, &master->reg_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(master->reg_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(contents);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY);
+
+    if (request.state == EC_INT_REQUEST_SUCCESS) {
+        if (copy_to_user((void __user *) data.data, contents, data.length))
+            return -EFAULT;
+    }
+    kfree(contents);
+
+    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Write a slave's registers.
+ */
+static int ec_ioctl_slave_reg_write(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_reg_t data;
+    ec_slave_t *slave;
+    uint8_t *contents;
+    ec_reg_request_t request;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    if (!data.length)
+        return 0;
+
+    if (!(contents = kmalloc(data.length, GFP_KERNEL))) {
+        EC_MASTER_ERR(master, "Failed to allocate %u bytes"
+                " for register data.\n", data.length);
+        return -ENOMEM;
+    }
+
+    if (copy_from_user(contents, (void __user *) data.data, data.length)) {
+        kfree(contents);
+        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);
+        kfree(contents);
+        return -EINVAL;
+    }
+
+    // init register request
+    INIT_LIST_HEAD(&request.list);
+    request.slave = slave;
+    request.dir = EC_DIR_OUTPUT;
+    request.data = contents;
+    request.offset = data.offset;
+    request.length = data.length;
+    request.state = EC_INT_REQUEST_QUEUED;
+
+    // schedule request.
+    list_add_tail(&request.list, &master->reg_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(master->reg_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(contents);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY);
+
+    kfree(contents);
+
+    return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+}
+
+/*****************************************************************************/
+
+/** Get slave configuration information.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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));
+
+    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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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;
+}
+
+/*****************************************************************************/
+
+/** Activates the master.
+ */
+static 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.
+ */
+static 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
+ */
+static 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 (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.
+ */
+static 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;
+
+    down(&master->io_sem);
+    ecrt_master_send(master);
+    up(&master->io_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Receive frames.
+ */
+static 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;
+
+    down(&master->io_sem);
+    ecrt_master_receive(master);
+    up(&master->io_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the master state.
+ */
+static 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;
+
+    if (unlikely(!ctx->requested))
+        return -EPERM;
+
+    ecrt_master_state(master, &data);
+
+    if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+        return -EFAULT;
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the master state.
+ */
+static 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 (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    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 app time.
+ */
+static 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. */
+        )
+{
+    ec_ioctl_app_time_t data;
+
+    if (unlikely(!ctx->requested))
+        return -EPERM;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    ecrt_master_application_time(master, data.app_time);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the reference clock.
+ */
+static 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;
+
+    down(&master->io_sem);
+    ecrt_master_sync_reference_clock(master);
+    up(&master->io_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Sync the slave clocks.
+ */
+static 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;
+
+    down(&master->io_sem);
+    ecrt_master_sync_slave_clocks(master);
+    up(&master->io_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the sync monitoring datagram.
+ */
+static 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;
+
+    down(&master->io_sem);
+    ecrt_master_sync_monitor_queue(master);
+    up(&master->io_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Processes the sync monitoring datagram.
+ */
+static 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;
+
+    down(&master->io_sem);
+    time_diff = ecrt_master_sync_monitor_process(master);
+    up(&master->io_sem);
+
+    if (copy_to_user((void __user *) arg, &time_diff, sizeof(time_diff)))
+        return -EFAULT;
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Reset configuration.
+ */
+static 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. */
+        )
+{
+    if (unlikely(!ctx->requested))
+        return -EPERM;
+
+    down(&master->master_sem);
+    ecrt_master_reset(master);
+    up(&master->master_sem);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Configure a sync manager.
+ */
+static 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) {
+            if (ecrt_slave_config_sync_manager(sc, i, data.syncs[i].dir,
+                        data.syncs[i].watchdog_mode)) {
+                ret = -EINVAL;
+                goto out_up;
+            }
+        }
+    }
+
+out_up:
+    up(&master->master_sem);
+out_return:
+    return ret;
+}
+
+/*****************************************************************************/
+
+/** Configure a slave's watchdogs.
+ */
+static 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.
+ */
+static 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); /** \fixme sc could be invalidated */
+
+    return ecrt_slave_config_pdo_assign_add(sc, data.sync_index, data.index);
+}
+
+/*****************************************************************************/
+
+/** Clears the PDO assignment.
+ */
+static 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); /** \fixme sc could be invalidated */
+
+    ecrt_slave_config_pdo_assign_clear(sc, data.sync_index);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Add an entry to a PDO's mapping.
+ */
+static 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); /** \fixme 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.
+ */
+static 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); /** \fixme sc could be invalidated */
+
+    ecrt_slave_config_pdo_mapping_clear(sc, data.index);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Registers a PDO entry.
+ */
+static 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); /** \fixme 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;
+}
+
+/*****************************************************************************/
+
+/** Sets the DC AssignActivate word and the sync signal times.
+ */
+static 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.
+ */
+static 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); /** \fixme 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;
+}
+
+/*****************************************************************************/
+
+/** Create an SDO request.
+ */
+static 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); /** \fixme 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 VoE handler.
+ */
+static 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); /** \fixme 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.
+ */
+static 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.
+ */
+static 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); /** \fixme 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 offset in the total process data.
+ */
+static 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 int) arg) {
+            up(&master->master_sem);
+            return offset;
+        }
+        offset += ecrt_domain_size(domain);
+    }
+
+    up(&master->master_sem);
+    return -ENOENT;
+}
+
+/*****************************************************************************/
+
+/** Process the domain.
+ */
+static 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 int) arg))) {
+        return -ENOENT;
+    }
+
+    ecrt_domain_process(domain);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Queue the domain.
+ */
+static 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 int) arg))) {
+        return -ENOENT;
+    }
+
+    ecrt_domain_queue(domain);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Get the domain state.
+ */
+static 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 timeout.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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;
+}
+
+/*****************************************************************************/
+
+/** Sets the VoE send header.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static 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.
+ */
+static int ec_ioctl_slave_foe_read(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_foe_t data;
+    ec_master_foe_request_t request;
+    int retval;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    ec_foe_request_init(&request.req, data.file_name);
+    ec_foe_request_read(&request.req);
+    ec_foe_request_alloc(&request.req, 10000); // FIXME
+
+    if (down_interruptible(&master->master_sem)) {
+        ec_foe_request_clear(&request.req);
+        return -EINTR;
+    }
+
+    if (!(request.slave = ec_master_find_slave(
+                    master, 0, data.slave_position))) {
+        up(&master->master_sem);
+        ec_foe_request_clear(&request.req);
+        EC_MASTER_ERR(master, "Slave %u does not exist!\n",
+                data.slave_position);
+        return -EINVAL;
+    }
+
+    // schedule request.
+    list_add_tail(&request.list, &request.slave->foe_requests);
+
+    up(&master->master_sem);
+
+    EC_SLAVE_DBG(request.slave, 1, "Scheduled FoE read request.\n");
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->foe_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            list_del(&request.list);
+            up(&master->master_sem);
+            ec_foe_request_clear(&request.req);
+            return -EINTR;
+        }
+        // request already processing: interrupt not possible.
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->foe_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    data.result = request.req.result;
+    data.error_code = request.req.error_code;
+
+    EC_SLAVE_DBG(request.slave, 1, "Read %zd bytes via FoE"
+            " (result = 0x%x).\n", request.req.data_size, request.req.result);
+
+    if (request.req.state != EC_INT_REQUEST_SUCCESS) {
+        data.data_size = 0;
+        retval = -EIO;
+    } else {
+        if (request.req.data_size > data.buffer_size) {
+            EC_MASTER_ERR(master, "Buffer too small.\n");
+            ec_foe_request_clear(&request.req);
+            return -EOVERFLOW;
+        }
+        data.data_size = request.req.data_size;
+        if (copy_to_user((void __user *) data.buffer,
+                    request.req.buffer, data.data_size)) {
+            ec_foe_request_clear(&request.req);
+            return -EFAULT;
+        }
+        retval = 0;
+    }
+
+    if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+        retval = -EFAULT;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Finished FoE read request.\n");
+
+    ec_foe_request_clear(&request.req);
+
+    return retval;
+}
+
+/*****************************************************************************/
+
+/** Write a file to a slave via FoE
+ */
+static int ec_ioctl_slave_foe_write(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg /**< ioctl() argument. */
+        )
+{
+    ec_ioctl_slave_foe_t data;
+    ec_master_foe_request_t request;
+    int retval;
+
+    if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+        return -EFAULT;
+    }
+
+    INIT_LIST_HEAD(&request.list);
+
+    ec_foe_request_init(&request.req, data.file_name);
+
+    if (ec_foe_request_alloc(&request.req, data.buffer_size)) {
+        ec_foe_request_clear(&request.req);
+        return -ENOMEM;
+    }
+    if (copy_from_user(request.req.buffer,
+                (void __user *) data.buffer, data.buffer_size)) {
+        ec_foe_request_clear(&request.req);
+        return -EFAULT;
+    }
+    request.req.data_size = data.buffer_size;
+    ec_foe_request_write(&request.req);
+
+    if (down_interruptible(&master->master_sem)) {
+        ec_foe_request_clear(&request.req);
+        return -EINTR;
+    }
+
+    if (!(request.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);
+        ec_foe_request_clear(&request.req);
+        return -EINVAL;
+    }
+
+    EC_SLAVE_DBG(request.slave, 1, "Scheduling FoE write request.\n");
+
+    // schedule FoE write request.
+    list_add_tail(&request.list, &request.slave->foe_requests);
+
+    up(&master->master_sem);
+
+    // wait for processing through FSM
+    if (wait_event_interruptible(request.slave->foe_queue,
+                request.req.state != EC_INT_REQUEST_QUEUED)) {
+        // interrupted by signal
+        down(&master->master_sem);
+        if (request.req.state == EC_INT_REQUEST_QUEUED) {
+            // abort request
+            list_del(&request.list);
+            up(&master->master_sem);
+            ec_foe_request_clear(&request.req);
+            return -EINTR;
+        }
+        up(&master->master_sem);
+    }
+
+    // wait until master FSM has finished processing
+    wait_event(request.slave->foe_queue,
+            request.req.state != EC_INT_REQUEST_BUSY);
+
+    data.result = request.req.result;
+    data.error_code = request.req.error_code;
+
+    retval = request.req.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
+
+    if (__copy_to_user((void __user *) arg, &data, sizeof(data))) {
+        retval = -EFAULT;
+    }
+
+    ec_foe_request_clear(&request.req);
+
+    EC_SLAVE_DBG(request.slave, 1, "Finished FoE write request.\n");
+
+    return retval;
+}
+
+/*****************************************************************************/
+
+/** Read an SoE IDN.
+ */
+static 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 %u 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.
+ */
+static 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;
+}
+
+/*****************************************************************************/
+
+#ifdef EC_IOCTL_RTDM
+#define EC_IOCTL ec_ioctl_rtdm
+#else
+#define EC_IOCTL ec_ioctl
+#endif
+
+/** Called when an ioctl() command is issued.
+ */
+long EC_IOCTL(ec_master_t *master, ec_ioctl_context_t *ctx,
+		unsigned int cmd, void *arg)
+{
+#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_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_SLAVES:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_sync_slaves(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_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_SDO_REQUEST:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_sc_create_sdo_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_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_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_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;
+}
+
+/*****************************************************************************/
--- a/master/ioctl.h	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/ioctl.h	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT Master.
  *
@@ -96,7 +96,7 @@
 #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_ACTIVATE              EC_IOR(0x21, size_t)
+#define EC_IOCTL_ACTIVATE              EC_IOR(0x21, ec_ioctl_master_activate_t)
 #define EC_IOCTL_DEACTIVATE             EC_IO(0x22)
 #define EC_IOCTL_SEND                   EC_IO(0x23)
 #define EC_IOCTL_RECEIVE                EC_IO(0x24)
@@ -574,6 +574,14 @@
 /*****************************************************************************/
 
 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;
@@ -689,6 +697,32 @@
 
 /*****************************************************************************/
 
+#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
--- a/master/master.c	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/master.c	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT Master.
  *
@@ -298,8 +298,24 @@
         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:
@@ -327,6 +343,10 @@
         ec_master_t *master /**< EtherCAT master */
         )
 {
+#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
--- a/master/master.h	Thu Sep 20 09:20:51 2012 +0200
+++ b/master/master.h	Thu Sep 20 15:28:25 2012 +0200
@@ -2,7 +2,7 @@
  *
  *  $Id$
  *
- *  Copyright (C) 2006-2008  Florian Pose, Ingenieurgemeinschaft IgH
+ *  Copyright (C) 2006-2012  Florian Pose, Ingenieurgemeinschaft IgH
  *
  *  This file is part of the IgH EtherCAT Master.
  *
@@ -55,6 +55,10 @@
 #include "fsm_master.h"
 #include "cdev.h"
 
+#ifdef EC_RTDM
+#include "rtdm.h"
+#endif
+
 /*****************************************************************************/
 
 /** Convenience macro for printing master-specific information to syslog.
@@ -183,6 +187,10 @@
     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_NUM_DEVICES]; /**< EtherCAT devices. */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/rtdm-ioctl.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,1 @@
+ioctl.c
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/rtdm.c	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,218 @@
+/*****************************************************************************
+ *
+ *  $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.
+ *
+ ****************************************************************************/
+
+#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;
+    ec_ioctl_context_t ioctl_ctx;
+} 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 *);
+
+/****************************************************************************/
+
+int ec_rtdm_dev_init(
+        ec_rtdm_dev_t *rtdm_dev,
+        ec_master_t *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;
+}
+
+/****************************************************************************/
+
+void ec_rtdm_dev_clear(
+        ec_rtdm_dev_t *rtdm_dev
+        )
+{
+    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.
+ */
+int ec_rtdm_open(
+        struct rtdm_dev_context *context,
+        rtdm_user_info_t *user_info,
+        int oflags
+        )
+{
+    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.
+ */
+int ec_rtdm_close(struct rtdm_dev_context *context,
+        rtdm_user_info_t *user_info)
+{
+    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.
+ */
+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.
+ */
+int ec_rtdm_mmap(ec_ioctl_context_t *ioctl_ctx, void **user_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;
+}
+
+/****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/rtdm.h	Thu Sep 20 15:28:25 2012 +0200
@@ -0,0 +1,50 @@
+/*****************************************************************************
+ *
+ *  $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.
+ *
+ ****************************************************************************/
+
+#ifndef __EC_RTDM_H__
+#define __EC_RTDM_H__
+
+#include "../include/ecrt.h" /* ec_master_t */
+
+/*****************************************************************************/
+
+struct rtdm_device;
+
+/*****************************************************************************/
+
+typedef struct ec_rtdm_dev {
+    ec_master_t *master;
+    struct rtdm_device *dev;
+} 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