Added interface to read/write register contents; re-worked register requests. stable-1.5
authorFlorian Pose <fp@igh-essen.com>
Wed, 14 Nov 2012 22:12:57 +0100
branchstable-1.5
changeset 2443 2c3ccdde3919
parent 2442 86ebf18a029f
child 2444 4b43c90142c8
Added interface to read/write register contents; re-worked register requests.
include/ecrt.h
lib/Makefile.am
lib/master.c
lib/reg_request.c
lib/reg_request.h
lib/slave_config.c
lib/slave_config.h
master/Kbuild.in
master/Makefile.am
master/fsm_master.c
master/fsm_master.h
master/fsm_slave.c
master/fsm_slave.h
master/ioctl.c
master/ioctl.h
master/master.c
master/master.h
master/reg_request.c
master/reg_request.h
master/slave.c
master/slave.h
master/slave_config.c
master/slave_config.h
tool/CommandRegRead.cpp
tool/CommandRegWrite.cpp
--- a/include/ecrt.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/include/ecrt.h	Wed Nov 14 22:12:57 2012 +0100
@@ -52,6 +52,10 @@
  *   ecrt_slave_config_emerg_size(), ecrt_slave_config_emerg_pop(),
  *   ecrt_slave_config_emerg_clear(), ecrt_slave_config_emerg_overruns() and
  *   the defines EC_HAVE_EMERGENCY and EC_COE_EMERGENCY_MSG_SIZE.
+ * - Added interface for direct EtherCAT register access: Added data type
+ *   ec_reg_request_t and methods ecrt_slave_config_create_reg_request(),
+ *   ecrt_reg_request_data(), ecrt_reg_request_state(),
+ *   ecrt_reg_request_write() and ecrt_reg_request_read().
  *
  * Changes in version 1.5:
  *
@@ -207,6 +211,9 @@
 struct ec_voe_handler;
 typedef struct ec_voe_handler ec_voe_handler_t; /**< \see ec_voe_handler. */
 
+struct ec_reg_request;
+typedef struct ec_reg_request ec_reg_request_t; /**< \see ec_sdo_request. */
+
 /*****************************************************************************/
 
 /** Master state.
@@ -1375,6 +1382,20 @@
         size_t size /**< Data size to reserve. */
         );
 
+/** Create a register request to exchange EtherCAT register contents during
+ * realtime operation.
+ *
+ * This interface should not be used to take over master functionality,
+ * instead it is intended for debugging and monitoring reasons.
+ *
+ * The created register request object is freed automatically when the master
+ * is released.
+ */
+ec_reg_request_t *ecrt_slave_config_create_reg_request(
+        ec_slave_config_t *sc, /**< Slave configuration. */
+        size_t size /**< Data size to reserve. */
+        );
+
 /** Outputs the state of the slave configuration.
  *
  * Stores the state information in the given \a state structure. The state
@@ -1745,6 +1766,75 @@
     ec_voe_handler_t *voe /**< VoE handler. */
     );
 
+/*****************************************************************************
+ * Register request methods.
+ ****************************************************************************/
+
+/** Access to the register request's data.
+ *
+ * This function returns a pointer to the request's internal memory.
+ *
+ * - After a read operation was successful, integer data can be evaluated using
+ *   the EC_READ_*() macros as usual. Example:
+ *   \code
+ *   uint16_t value = EC_READ_U16(ecrt_reg_request_data(reg_request)));
+ *   \endcode
+ * - If a write operation shall be triggered, the data have to be written to
+ *   the internal memory. Use the EC_WRITE_*() macros, if you are writing
+ *   integer data. Be sure, that the data fit into the memory. The memory size
+ *   is a parameter of ecrt_slave_config_create_reg_request().
+ *   \code
+ *   EC_WRITE_U16(ecrt_reg_request_data(reg_request), 0xFFFF);
+ *   \endcode
+ *
+ * \return Pointer to the internal memory.
+ */
+uint8_t *ecrt_reg_request_data(
+        ec_reg_request_t *req /**< Register request. */
+        );
+
+/** Get the current state of the register request.
+ *
+ * \return Request state.
+ */
+#ifdef __KERNEL__
+ec_request_state_t ecrt_reg_request_state(
+        const ec_reg_request_t *req /**< Register request. */
+    );
+#else
+ec_request_state_t ecrt_reg_request_state(
+        ec_reg_request_t *req /**< Register request. */
+    );
+#endif
+
+/** Schedule an register write operation.
+ *
+ * \attention This method may not be called while ecrt_reg_request_state()
+ * returns EC_REQUEST_BUSY.
+ *
+ * \attention The \a size parameter is truncated to the size given at request
+ * creation.
+ */
+void ecrt_reg_request_write(
+        ec_reg_request_t *req, /**< Register request. */
+        uint16_t address, /**< Register address. */
+        size_t size /**< Size to write. */
+        );
+
+/** Schedule a register read operation.
+ *
+ * \attention This method may not be called while ecrt_reg_request_state()
+ * returns EC_REQUEST_BUSY.
+ *
+ * \attention The \a size parameter is truncated to the size given at request
+ * creation.
+ */
+void ecrt_reg_request_read(
+        ec_reg_request_t *req, /**< Register request. */
+        uint16_t address, /**< Register address. */
+        size_t size /**< Size to write. */
+        );
+
 /*****************************************************************************/
 
 #ifdef __cplusplus
--- a/lib/Makefile.am	Wed Nov 14 22:08:32 2012 +0100
+++ b/lib/Makefile.am	Wed Nov 14 22:12:57 2012 +0100
@@ -36,6 +36,7 @@
 	common.c \
 	domain.c \
 	master.c \
+	reg_request.c \
 	sdo_request.c \
 	slave_config.c \
 	voe_handler.c
@@ -43,6 +44,7 @@
 noinst_HEADERS = \
 	domain.h \
 	master.h \
+	reg_request.h \
 	sdo_request.h \
 	slave_config.h \
 	voe_handler.h
--- a/lib/master.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/lib/master.c	Wed Nov 14 22:12:57 2012 +0100
@@ -192,6 +192,7 @@
     sc->alias = alias;
     sc->position = position;
     sc->first_sdo_request = NULL;
+    sc->first_reg_request = NULL;
     sc->first_voe_handler = NULL;
 
     ec_master_add_slave_config(master, sc);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/reg_request.c	Wed Nov 14 22:12:57 2012 +0100
@@ -0,0 +1,138 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2012  Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ *  This file is part of the IgH EtherCAT master userspace library.
+ *
+ *  The IgH EtherCAT master userspace library is free software; you can
+ *  redistribute it and/or modify it under the terms of the GNU Lesser General
+ *  Public License as published by the Free Software Foundation; version 2.1
+ *  of the License.
+ *
+ *  The IgH EtherCAT master userspace library is distributed in the hope that
+ *  it will be useful, but WITHOUT ANY WARRANTY; without even the implied
+ *  warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public License
+ *  along with the IgH EtherCAT master userspace library. If not, see
+ *  <http://www.gnu.org/licenses/>.
+ *
+ *  ---
+ *
+ *  The license mentioned above concerns the source code only. Using the
+ *  EtherCAT technology and brand is only permitted in compliance with the
+ *  industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/** \file
+ * EtherCAT register request functions.
+ */
+
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ioctl.h"
+#include "reg_request.h"
+#include "slave_config.h"
+#include "master.h"
+
+/*****************************************************************************/
+
+void ec_reg_request_clear(ec_reg_request_t *reg)
+{
+    if (reg->data) {
+        free(reg->data);
+    }
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+uint8_t *ecrt_reg_request_data(ec_reg_request_t *reg)
+{
+    return reg->data;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_reg_request_state(ec_reg_request_t *reg)
+{
+    ec_ioctl_reg_request_t io;
+    int ret;
+
+    io.config_index = reg->config->index;
+    io.request_index = reg->index;
+
+    ret = ioctl(reg->config->master->fd, EC_IOCTL_REG_REQUEST_STATE, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to get register request state: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        return EC_REQUEST_ERROR;
+    }
+
+    if (io.new_data) { // new data waiting to be copied
+
+        io.data = reg->data;
+        io.mem_size = reg->mem_size;
+
+        ret = ioctl(reg->config->master->fd,
+                EC_IOCTL_REG_REQUEST_DATA, &io);
+        if (EC_IOCTL_IS_ERROR(ret)) {
+            fprintf(stderr, "Failed to get register data: %s\n",
+                    strerror(EC_IOCTL_ERRNO(ret)));
+            return EC_REQUEST_ERROR;
+        }
+    }
+
+    return io.state;
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_write(ec_reg_request_t *reg, uint16_t address,
+        size_t size)
+{
+    ec_ioctl_reg_request_t io;
+    int ret;
+
+    io.config_index = reg->config->index;
+    io.request_index = reg->index;
+    io.data = reg->data;
+    io.address = address;
+    io.transfer_size = size;
+
+    ret = ioctl(reg->config->master->fd, EC_IOCTL_REG_REQUEST_WRITE, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to command an register write operation: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_read(ec_reg_request_t *reg, uint16_t address,
+        size_t size)
+{
+    ec_ioctl_reg_request_t io;
+    int ret;
+
+    io.config_index = reg->config->index;
+    io.request_index = reg->index;
+    io.address = address;
+    io.transfer_size = size;
+
+    ret = ioctl(reg->config->master->fd, EC_IOCTL_REG_REQUEST_READ, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to command an register read operation: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+    }
+}
+
+/*****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/reg_request.h	Wed Nov 14 22:12:57 2012 +0100
@@ -0,0 +1,47 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 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.
+ *
+ *****************************************************************************/
+
+#include "include/ecrt.h"
+
+/*****************************************************************************/
+
+struct ec_reg_request {
+    ec_reg_request_t *next; /**< List header. */
+    ec_slave_config_t *config; /**< Parent slave configuration. */
+    unsigned int index; /**< Request index (identifier). */
+    uint8_t *data; /**< Data memory. */
+    size_t mem_size; /**< Size of \a data. */
+};
+
+/*****************************************************************************/
+
+void ec_reg_request_clear(ec_reg_request_t *);
+
+/*****************************************************************************/
--- a/lib/slave_config.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/lib/slave_config.c	Wed Nov 14 22:12:57 2012 +0100
@@ -37,6 +37,7 @@
 #include "slave_config.h"
 #include "domain.h"
 #include "sdo_request.h"
+#include "reg_request.h"
 #include "voe_handler.h"
 #include "master.h"
 
@@ -45,6 +46,7 @@
 void ec_slave_config_clear(ec_slave_config_t *sc)
 {
     ec_sdo_request_t *r, *next_r;
+    ec_reg_request_t *e, *next_e;
     ec_voe_handler_t *v, *next_v;
 
     r = sc->first_sdo_request;
@@ -54,6 +56,12 @@
         r = next_r;
     }
 
+    e = sc->first_reg_request;
+    while (e) {
+        next_e = e->next;
+        ec_reg_request_clear(e);
+        e = next_e;
+    }
 
     v = sc->first_voe_handler;
     while (v) {
@@ -551,6 +559,71 @@
 
 /*****************************************************************************/
 
+void ec_slave_config_add_reg_request(ec_slave_config_t *sc,
+        ec_reg_request_t *reg)
+{
+    if (sc->first_reg_request) {
+        ec_reg_request_t *r = sc->first_reg_request;
+        while (r->next) {
+            r = r->next;
+        }
+        r->next = reg;
+    } else {
+        sc->first_reg_request = reg;
+    }
+}
+
+/*****************************************************************************/
+
+ec_reg_request_t *ecrt_slave_config_create_reg_request(ec_slave_config_t *sc,
+        size_t size)
+{
+    ec_ioctl_reg_request_t io;
+    ec_reg_request_t *reg;
+    int ret;
+
+    reg = malloc(sizeof(ec_reg_request_t));
+    if (!reg) {
+        fprintf(stderr, "Failed to allocate memory.\n");
+        return NULL;
+    }
+
+    if (size) {
+        reg->data = malloc(size);
+        if (!reg->data) {
+            fprintf(stderr, "Failed to allocate %u bytes of register data"
+                    " memory.\n", size);
+            free(reg);
+            return 0;
+        }
+    } else {
+        reg->data = NULL;
+    }
+
+    io.config_index = sc->index;
+    io.mem_size = size;
+
+    ret = ioctl(sc->master->fd, EC_IOCTL_SC_REG_REQUEST, &io);
+    if (EC_IOCTL_IS_ERROR(ret)) {
+        fprintf(stderr, "Failed to create register request: %s\n",
+                strerror(EC_IOCTL_ERRNO(ret)));
+        ec_reg_request_clear(reg);
+        free(reg);
+        return NULL;
+    }
+
+    reg->next = NULL;
+    reg->config = sc;
+    reg->index = io.request_index;
+    reg->mem_size = size;
+
+    ec_slave_config_add_reg_request(sc, reg);
+
+    return reg;
+}
+
+/*****************************************************************************/
+
 void ec_slave_config_add_voe_handler(ec_slave_config_t *sc,
         ec_voe_handler_t *voe)
 {
--- a/lib/slave_config.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/lib/slave_config.h	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -39,6 +39,7 @@
     uint16_t alias;
     uint16_t position;
     ec_sdo_request_t *first_sdo_request;
+    ec_reg_request_t *first_reg_request;
     ec_voe_handler_t *first_voe_handler;
 };
 
--- a/master/Kbuild.in	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/Kbuild.in	Wed Nov 14 22:12:57 2012 +0100
@@ -60,6 +60,7 @@
 	pdo.o \
 	pdo_entry.o \
 	pdo_list.o \
+	reg_request.o \
 	sdo.o \
 	sdo_entry.o \
 	sdo_request.o \
--- a/master/Makefile.am	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/Makefile.am	Wed Nov 14 22:12:57 2012 +0100
@@ -60,6 +60,7 @@
 	pdo.c pdo.h \
 	pdo_entry.c pdo_entry.h \
 	pdo_list.c pdo_list.h \
+	reg_request.c reg_request.h \
 	rtdm-ioctl.c \
 	rtdm.c rtdm.h \
 	sdo.c sdo.h \
--- a/master/fsm_master.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/fsm_master.c	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -65,7 +65,6 @@
 void ec_fsm_master_state_write_sii(ec_fsm_master_t *);
 void ec_fsm_master_state_sdo_dictionary(ec_fsm_master_t *);
 void ec_fsm_master_state_sdo_request(ec_fsm_master_t *);
-void ec_fsm_master_state_reg_request(ec_fsm_master_t *);
 
 void ec_fsm_master_enter_clear_addresses(ec_fsm_master_t *);
 void ec_fsm_master_enter_write_system_times(ec_fsm_master_t *);
@@ -404,62 +403,6 @@
 
 /*****************************************************************************/
 
-/** Check for pending register requests and process one.
- *
- * \return non-zero, if a register request is processed.
- */
-int ec_fsm_master_action_process_register(
-        ec_fsm_master_t *fsm /**< Master state machine. */
-        )
-{
-    ec_master_t *master = fsm->master;
-    ec_reg_request_t *request;
-
-    // search the first request to be processed
-    while (!list_empty(&master->reg_requests)) {
-
-        // get first request
-        request = list_entry(master->reg_requests.next,
-                ec_reg_request_t, list);
-        list_del_init(&request->list); // dequeue
-        request->state = EC_INT_REQUEST_BUSY;
-
-        // found pending request; process it!
-        EC_SLAVE_DBG(request->slave, 1, "Processing register request, "
-                "offset 0x%04x, length %zu...\n",
-                request->offset, request->length);
-
-        if (request->length > fsm->datagram->mem_size) {
-            EC_MASTER_ERR(master, "Request length (%zu) exceeds maximum "
-                    "datagram size (%zu)!\n", request->length,
-                    fsm->datagram->mem_size);
-            request->state = EC_INT_REQUEST_FAILURE;
-            wake_up(&master->reg_queue);
-            continue;
-        }
-
-        fsm->reg_request = request;
-
-        if (request->dir == EC_DIR_INPUT) {
-            ec_datagram_fprd(fsm->datagram, request->slave->station_address,
-                    request->offset, request->length);
-            ec_datagram_zero(fsm->datagram);
-        } else {
-            ec_datagram_fpwr(fsm->datagram, request->slave->station_address,
-                    request->offset, request->length);
-            memcpy(fsm->datagram->data, request->data, request->length);
-        }
-        fsm->datagram->device_index = request->slave->device_index;
-        fsm->retries = EC_FSM_RETRIES;
-        fsm->state = ec_fsm_master_state_reg_request;
-        return 1;
-    }
-
-    return 0;
-}
-
-/*****************************************************************************/
-
 /** Check for pending SDO requests and process one.
  *
  * \return non-zero, if an SDO request is processed.
@@ -509,7 +452,6 @@
     return 0;
 }
 
-
 /*****************************************************************************/
 
 /** Master action: IDLE.
@@ -563,12 +505,9 @@
     }
 
     // check for pending SII write operations.
-    if (ec_fsm_master_action_process_sii(fsm))
+    if (ec_fsm_master_action_process_sii(fsm)) {
         return; // SII write request found
-
-    // check for pending register requests.
-    if (ec_fsm_master_action_process_register(fsm))
-        return; // register request processing
+	}
 
     ec_fsm_master_restart(fsm);
 }
@@ -1276,64 +1215,11 @@
     EC_SLAVE_DBG(fsm->slave, 1, "Finished internal SDO request.\n");
 
     // check for another SDO request
-    if (ec_fsm_master_action_process_sdo(fsm))
+    if (ec_fsm_master_action_process_sdo(fsm)) {
         return; // processing another request
+    }
 
     ec_fsm_master_restart(fsm);
 }
 
 /*****************************************************************************/
-
-/** Master state: REG REQUEST.
- */
-void ec_fsm_master_state_reg_request(
-        ec_fsm_master_t *fsm /**< Master state machine. */
-        )
-{
-    ec_master_t *master = fsm->master;
-    ec_datagram_t *datagram = fsm->datagram;
-    ec_reg_request_t *request = fsm->reg_request;
-
-    if (datagram->state != EC_DATAGRAM_RECEIVED) {
-        EC_MASTER_ERR(master, "Failed to receive register"
-                " request datagram: ");
-        ec_datagram_print_state(datagram);
-        request->state = EC_INT_REQUEST_FAILURE;
-        wake_up(&master->reg_queue);
-        ec_fsm_master_restart(fsm);
-        return;
-    }
-
-    if (datagram->working_counter == 1) {
-        if (request->dir == EC_DIR_INPUT) { // read request
-            if (request->data)
-                kfree(request->data);
-            request->data = kmalloc(request->length, GFP_KERNEL);
-            if (!request->data) {
-                EC_MASTER_ERR(master, "Failed to allocate %zu bytes"
-                        " of memory for register data.\n", request->length);
-                request->state = EC_INT_REQUEST_FAILURE;
-                wake_up(&master->reg_queue);
-                ec_fsm_master_restart(fsm);
-                return;
-            }
-            memcpy(request->data, datagram->data, request->length);
-        }
-
-        request->state = EC_INT_REQUEST_SUCCESS;
-        EC_SLAVE_DBG(request->slave, 1, "Register request successful.\n");
-    } else {
-        request->state = EC_INT_REQUEST_FAILURE;
-        EC_MASTER_ERR(master, "Register request failed.\n");
-    }
-
-    wake_up(&master->reg_queue);
-
-    // check for another register request
-    if (ec_fsm_master_action_process_register(fsm))
-        return; // processing another request
-
-    ec_fsm_master_restart(fsm);
-}
-
-/*****************************************************************************/
--- a/master/fsm_master.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/fsm_master.h	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -61,20 +61,6 @@
 
 /*****************************************************************************/
 
-/** Register request.
- */
-typedef struct {
-    struct list_head list; /**< List head. */
-    ec_slave_t *slave; /**< EtherCAT slave. */
-    ec_direction_t dir; /**< Direction. */
-    uint16_t offset; /**< Register address. */
-    size_t length; /**< Number of bytes. */
-    uint8_t *data; /**< Data to write / memory for read data. */
-    ec_internal_request_state_t state; /**< State of the request. */
-} ec_reg_request_t;
-
-/*****************************************************************************/
-
 /** Slave/SDO request record for master's SDO request list.
  */
 typedef struct {
@@ -132,7 +118,6 @@
     ec_sii_write_request_t *sii_request; /**< SII write request */
     off_t sii_index; /**< index to SII write request data */
     ec_sdo_request_t *sdo_request; /**< SDO request to process. */
-    ec_reg_request_t *reg_request; /**< Register request to process. */
 
     ec_fsm_coe_t fsm_coe; /**< CoE state machine */
     ec_fsm_pdo_t fsm_pdo; /**< PDO configuration state machine. */
--- a/master/fsm_slave.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/fsm_slave.c	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -36,6 +36,7 @@
 #include "globals.h"
 #include "master.h"
 #include "mailbox.h"
+#include "slave_config.h"
 
 #include "fsm_slave.h"
 
@@ -45,6 +46,8 @@
 void ec_fsm_slave_state_ready(ec_fsm_slave_t *);
 int ec_fsm_slave_action_process_sdo(ec_fsm_slave_t *);
 void ec_fsm_slave_state_sdo_request(ec_fsm_slave_t *);
+int ec_fsm_slave_action_process_reg(ec_fsm_slave_t *);
+void ec_fsm_slave_state_reg_request(ec_fsm_slave_t *);
 int ec_fsm_slave_action_process_foe(ec_fsm_slave_t *);
 void ec_fsm_slave_state_foe_request(ec_fsm_slave_t *);
 int ec_fsm_slave_action_process_soe(ec_fsm_slave_t *);
@@ -137,7 +140,6 @@
     // do nothing
 }
 
-
 /*****************************************************************************/
 
 /** Slave state: READY.
@@ -151,6 +153,11 @@
         return;
     }
 
+    // Check for pending external register requests
+    if (ec_fsm_slave_action_process_reg(fsm)) {
+        return;
+    }
+
     // Check for pending FoE requests
     if (ec_fsm_slave_action_process_foe(fsm)) {
         return;
@@ -176,7 +183,8 @@
     ec_master_sdo_request_t *request, *next;
 
     // search the first external request to be processed
-    list_for_each_entry_safe(request, next, &slave->slave_sdo_requests, list) {
+    list_for_each_entry_safe(request, next,
+            &slave->slave_sdo_requests, list) {
 
         list_del_init(&request->list); // dequeue
         if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
@@ -229,6 +237,7 @@
         ec_master_queue_external_datagram(fsm->slave->master, fsm->datagram);
         return;
     }
+
     if (!ec_fsm_coe_success(&fsm->fsm_coe)) {
         EC_SLAVE_ERR(slave, "Failed to process SDO request.\n");
         request->state = EC_INT_REQUEST_FAILURE;
@@ -250,6 +259,114 @@
 
 /*****************************************************************************/
 
+/** Check for pending register requests and process one.
+ *
+ * \return non-zero, if a register request is processed.
+ */
+int ec_fsm_slave_action_process_reg(
+        ec_fsm_slave_t *fsm /**< Slave state machine. */
+        )
+{
+    ec_slave_t *slave = fsm->slave;
+    ec_reg_request_t *reg, *next;
+
+    fsm->reg_request = NULL;
+
+    if (slave->config) {
+        // search the first internal register request to be processed
+        list_for_each_entry(reg, &slave->config->reg_requests, list) {
+            if (reg->state == EC_INT_REQUEST_QUEUED) {
+                fsm->reg_request = reg;
+                break;
+            }
+        }
+    }
+
+    if (!fsm->reg_request) {
+        // search the first external request to be processed
+        list_for_each_entry_safe(reg, next, &slave->reg_requests, list) {
+            list_del_init(&reg->list); // dequeue
+            fsm->reg_request = reg;
+            break;
+        }
+    }
+
+    if (!fsm->reg_request) { // no register request to process
+        return 0;
+    }
+
+    if (slave->current_state & EC_SLAVE_STATE_ACK_ERR) {
+        EC_SLAVE_WARN(slave, "Aborting register request,"
+                " slave has error flag set.\n");
+        reg->state = EC_INT_REQUEST_FAILURE;
+        wake_up(&slave->reg_queue);
+        fsm->state = ec_fsm_slave_state_idle;
+        return 1;
+    }
+
+    // Found pending register request. Execute it!
+    EC_SLAVE_DBG(slave, 1, "Processing register request...\n");
+
+    reg->state = EC_INT_REQUEST_BUSY;
+
+    // Start register access
+    if (reg->dir == EC_DIR_INPUT) {
+        ec_datagram_fprd(fsm->datagram, slave->station_address,
+                reg->address, reg->transfer_size);
+        ec_datagram_zero(fsm->datagram);
+    } else {
+        ec_datagram_fpwr(fsm->datagram, slave->station_address,
+                reg->address, reg->transfer_size);
+        memcpy(fsm->datagram->data, reg->data, reg->transfer_size);
+    }
+    fsm->datagram->device_index = slave->device_index;
+    ec_master_queue_external_datagram(slave->master, fsm->datagram);
+    fsm->state = ec_fsm_slave_state_reg_request;
+    return 1;
+}
+
+/*****************************************************************************/
+
+/** Slave state: Register request.
+ */
+void ec_fsm_slave_state_reg_request(
+        ec_fsm_slave_t *fsm /**< Slave state machine. */
+        )
+{
+    ec_slave_t *slave = fsm->slave;
+    ec_reg_request_t *reg = fsm->reg_request;
+
+    if (fsm->datagram->state != EC_DATAGRAM_RECEIVED) {
+        EC_SLAVE_ERR(slave, "Failed to receive register"
+                " request datagram: ");
+        ec_datagram_print_state(fsm->datagram);
+        reg->state = EC_INT_REQUEST_FAILURE;
+        wake_up(&slave->reg_queue);
+        fsm->state = ec_fsm_slave_state_ready;
+        return;
+    }
+
+    if (fsm->datagram->working_counter == 1) {
+        if (reg->dir == EC_DIR_INPUT) { // read request
+            memcpy(reg->data, fsm->datagram->data, reg->transfer_size);
+        }
+
+        reg->state = EC_INT_REQUEST_SUCCESS;
+        EC_SLAVE_DBG(slave, 1, "Register request successful.\n");
+    } else {
+        reg->state = EC_INT_REQUEST_FAILURE;
+        ec_datagram_print_state(fsm->datagram);
+        EC_SLAVE_ERR(slave, "Register request failed"
+                " (working counter is %u).\n",
+                fsm->datagram->working_counter);
+    }
+
+    wake_up(&slave->reg_queue);
+    fsm->state = ec_fsm_slave_state_ready;
+}
+
+/*****************************************************************************/
+
 /** Check for pending FoE requests and process one.
  *
  * \return non-zero, if an FoE request is processed.
--- a/master/fsm_slave.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/fsm_slave.h	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -29,20 +29,24 @@
 
 /**
    \file
-   EtherCAT slave request (SDO) state machine.
+   EtherCAT slave request state machine.
 */
 
 /*****************************************************************************/
+
 #ifndef __EC_FSM_SLAVE_H__
 #define __EC_FSM_SLAVE_H__
 
 #include "globals.h"
 #include "datagram.h"
 #include "sdo_request.h"
+#include "reg_request.h"
 #include "fsm_coe.h"
 #include "fsm_foe.h"
 #include "fsm_soe.h"
 
+/*****************************************************************************/
+
 typedef struct ec_fsm_slave ec_fsm_slave_t; /**< \see ec_fsm_slave */
 
 /** Finite state machine of an EtherCAT slave.
@@ -53,6 +57,7 @@
 
     void (*state)(ec_fsm_slave_t *); /**< master state function */
     ec_sdo_request_t *sdo_request; /**< SDO request to process. */
+    ec_reg_request_t *reg_request; /**< Register request to process. */
     ec_foe_request_t *foe_request; /**< FoE request to process. */
     off_t foe_index; /**< index to FoE write request data */
     ec_soe_request_t *soe_request; /**< SoE request to process. */
--- a/master/ioctl.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/ioctl.c	Wed Nov 14 22:12:57 2012 +0100
@@ -977,51 +977,48 @@
         void *arg /**< ioctl() argument. */
         )
 {
-    ec_ioctl_slave_reg_t data;
+    ec_ioctl_slave_reg_t io;
     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)
+    int ret;
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    if (!io.size) {
         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))
+    }
+
+    // init register request
+    ret = ec_reg_request_init(&request, io.size);
+    if (ret) {
+        return ret;
+    }
+
+    ecrt_reg_request_read(&request, io.address, io.size);
+
+    if (down_interruptible(&master->master_sem)) {
+        ec_reg_request_clear(&request);
         return -EINTR;
+    }
 
     if (!(slave = ec_master_find_slave(
-                    master, 0, data.slave_position))) {
-        up(&master->master_sem);
+                    master, 0, io.slave_position))) {
+        up(&master->master_sem);
+        ec_reg_request_clear(&request);
         EC_MASTER_ERR(master, "Slave %u does not exist!\n",
-                data.slave_position);
+                io.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);
+    list_add_tail(&request.list, &slave->reg_requests);
 
     up(&master->master_sem);
 
     // wait for processing through FSM
-    if (wait_event_interruptible(master->reg_queue,
+    if (wait_event_interruptible(slave->reg_queue,
                 request.state != EC_INT_REQUEST_QUEUED)) {
         // interrupted by signal
         down(&master->master_sem);
@@ -1029,20 +1026,21 @@
             // abort request
             list_del(&request.list);
             up(&master->master_sem);
-            kfree(contents);
+            ec_reg_request_clear(&request);
             return -EINTR;
         }
         up(&master->master_sem);
     }
 
     // wait until master FSM has finished processing
-    wait_event(master->reg_queue, request.state != EC_INT_REQUEST_BUSY);
+    wait_event(slave->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))
+        if (copy_to_user((void __user *) io.data, request.data, io.size)) {
             return -EFAULT;
-    }
-    kfree(contents);
+        }
+    }
+    ec_reg_request_clear(&request);
 
     return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
 }
@@ -1056,57 +1054,53 @@
         void *arg /**< ioctl() argument. */
         )
 {
-    ec_ioctl_slave_reg_t data;
+    ec_ioctl_slave_reg_t io;
     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)
+    int ret;
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    if (!io.size) {
         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))
+    }
+
+    // init register request
+    ret = ec_reg_request_init(&request, io.size);
+    if (ret) {
+        return ret;
+    }
+
+    if (copy_from_user(request.data, (void __user *) io.data, io.size)) {
+        ec_reg_request_clear(&request);
+        return -EFAULT;
+    }
+
+    ecrt_reg_request_write(&request, io.address, io.size);
+
+    if (down_interruptible(&master->master_sem)) {
+        ec_reg_request_clear(&request);
         return -EINTR;
+    }
 
     if (!(slave = ec_master_find_slave(
-                    master, 0, data.slave_position))) {
-        up(&master->master_sem);
+                    master, 0, io.slave_position))) {
+        up(&master->master_sem);
+        ec_reg_request_clear(&request);
         EC_MASTER_ERR(master, "Slave %u does not exist!\n",
-                data.slave_position);
-        kfree(contents);
+                io.slave_position);
         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);
+    list_add_tail(&request.list, &slave->reg_requests);
 
     up(&master->master_sem);
 
     // wait for processing through FSM
-    if (wait_event_interruptible(master->reg_queue,
+    if (wait_event_interruptible(slave->reg_queue,
                 request.state != EC_INT_REQUEST_QUEUED)) {
         // interrupted by signal
         down(&master->master_sem);
@@ -1114,16 +1108,16 @@
             // abort request
             list_del(&request.list);
             up(&master->master_sem);
-            kfree(contents);
+            ec_reg_request_clear(&request);
             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);
+    wait_event(slave->reg_queue, request.state != EC_INT_REQUEST_BUSY);
+
+    ec_reg_request_clear(&request);
 
     return request.state == EC_INT_REQUEST_SUCCESS ? 0 : -EIO;
 }
@@ -1608,7 +1602,7 @@
 
         /* 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,
@@ -1617,10 +1611,10 @@
         }
 
 #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);
+        /* 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);
@@ -2488,6 +2482,58 @@
 
 /*****************************************************************************/
 
+/** Create a register request.
+ */
+static int ec_ioctl_sc_create_reg_request(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_reg_request_t io;
+    ec_slave_config_t *sc;
+    ec_reg_request_t *reg;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    io.request_index = 0;
+
+    if (down_interruptible(&master->master_sem)) {
+        return -EINTR;
+    }
+
+    sc = ec_master_get_config(master, io.config_index);
+    if (!sc) {
+        up(&master->master_sem);
+        return -ENOENT;
+    }
+
+    list_for_each_entry(reg, &sc->reg_requests, list) {
+        io.request_index++;
+    }
+
+    up(&master->master_sem); /** \fixme sc could be invalidated */
+
+    reg = ecrt_slave_config_create_reg_request_err(sc, io.mem_size);
+    if (IS_ERR(reg)) {
+        return PTR_ERR(reg);
+    }
+
+    if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
 /** Create a VoE handler.
  */
 static int ec_ioctl_sc_create_voe_handler(
@@ -2979,6 +3025,181 @@
 
 /*****************************************************************************/
 
+/** Read register data.
+ */
+static int ec_ioctl_reg_request_data(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_reg_request_t io;
+    ec_slave_config_t *sc;
+    ec_reg_request_t *reg;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    if (io.mem_size <= 0) {
+        return 0;
+    }
+
+    /* no locking of master_sem needed, because neither sc nor reg will not be
+     * deleted in the meantime. */
+
+    if (!(sc = ec_master_get_config(master, io.config_index))) {
+        return -ENOENT;
+    }
+
+    if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+        return -ENOENT;
+    }
+
+    if (copy_to_user((void __user *) io.data, ecrt_reg_request_data(reg),
+                min(reg->mem_size, io.mem_size))) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an register request's state.
+ */
+static int ec_ioctl_reg_request_state(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_reg_request_t io;
+    ec_slave_config_t *sc;
+    ec_reg_request_t *reg;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    /* no locking of master_sem needed, because neither sc nor reg will not be
+     * deleted in the meantime. */
+
+    if (!(sc = ec_master_get_config(master, io.config_index))) {
+        return -ENOENT;
+    }
+
+    if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+        return -ENOENT;
+    }
+
+    io.state = ecrt_reg_request_state(reg);
+    io.new_data = io.state == EC_REQUEST_SUCCESS && reg->dir == EC_DIR_INPUT;
+
+    if (copy_to_user((void __user *) arg, &io, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register write operation.
+ */
+static int ec_ioctl_reg_request_write(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_reg_request_t io;
+    ec_slave_config_t *sc;
+    ec_reg_request_t *reg;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    /* no locking of master_sem needed, because neither sc nor reg will not be
+     * deleted in the meantime. */
+
+    if (!(sc = ec_master_get_config(master, io.config_index))) {
+        return -ENOENT;
+    }
+
+    if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+        return -ENOENT;
+    }
+
+    if (io.transfer_size > reg->mem_size) {
+        return -EOVERFLOW;
+    }
+
+    if (copy_from_user(reg->data, (void __user *) io.data,
+                io.transfer_size)) {
+        return -EFAULT;
+    }
+
+    ecrt_reg_request_write(reg, io.address, io.transfer_size);
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an register read operation.
+ */
+static int ec_ioctl_reg_request_read(
+        ec_master_t *master, /**< EtherCAT master. */
+        void *arg, /**< ioctl() argument. */
+        ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+        )
+{
+    ec_ioctl_reg_request_t io;
+    ec_slave_config_t *sc;
+    ec_reg_request_t *reg;
+
+    if (unlikely(!ctx->requested)) {
+        return -EPERM;
+    }
+
+    if (copy_from_user(&io, (void __user *) arg, sizeof(io))) {
+        return -EFAULT;
+    }
+
+    /* no locking of master_sem needed, because neither sc nor reg will not be
+     * deleted in the meantime. */
+
+    if (!(sc = ec_master_get_config(master, io.config_index))) {
+        return -ENOENT;
+    }
+
+    if (!(reg = ec_slave_config_find_reg_request(sc, io.request_index))) {
+        return -ENOENT;
+    }
+
+    if (io.transfer_size > reg->mem_size) {
+        return -EOVERFLOW;
+    }
+
+    ecrt_reg_request_read(reg, io.address, io.transfer_size);
+    return 0;
+}
+
+/*****************************************************************************/
+
 /** Sets the VoE send header.
  */
 static int ec_ioctl_voe_send_header(
@@ -3548,7 +3769,7 @@
 /** Called when an ioctl() command is issued.
  */
 long EC_IOCTL(ec_master_t *master, ec_ioctl_context_t *ctx,
-		unsigned int cmd, void *arg)
+        unsigned int cmd, void *arg)
 {
 #if DEBUG_LATENCY
     cycles_t a = get_cycles(), b;
@@ -3876,6 +4097,13 @@
             }
             ret = ec_ioctl_sc_create_sdo_request(master, arg, ctx);
             break;
+        case EC_IOCTL_SC_REG_REQUEST:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_sc_create_reg_request(master, arg, ctx);
+            break;
         case EC_IOCTL_SC_VOE:
             if (!ctx->writable) {
                 ret = -EPERM;
@@ -3947,6 +4175,26 @@
         case EC_IOCTL_SDO_REQUEST_DATA:
             ret = ec_ioctl_sdo_request_data(master, arg, ctx);
             break;
+        case EC_IOCTL_REG_REQUEST_DATA:
+            ret = ec_ioctl_reg_request_data(master, arg, ctx);
+            break;
+        case EC_IOCTL_REG_REQUEST_STATE:
+            ret = ec_ioctl_reg_request_state(master, arg, ctx);
+            break;
+        case EC_IOCTL_REG_REQUEST_WRITE:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_reg_request_write(master, arg, ctx);
+            break;
+        case EC_IOCTL_REG_REQUEST_READ:
+            if (!ctx->writable) {
+                ret = -EPERM;
+                break;
+            }
+            ret = ec_ioctl_reg_request_read(master, arg, ctx);
+            break;
         case EC_IOCTL_VOE_SEND_HEADER:
             if (!ctx->writable) {
                 ret = -EPERM;
--- a/master/ioctl.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/ioctl.h	Wed Nov 14 22:12:57 2012 +0100
@@ -56,7 +56,7 @@
  *
  * Increment this when changing the ioctl interface!
  */
-#define EC_IOCTL_VERSION_MAGIC 21
+#define EC_IOCTL_VERSION_MAGIC 22
 
 // Command-line tool
 #define EC_IOCTL_MODULE                EC_IOR(0x00, ec_ioctl_module_t)
@@ -122,27 +122,32 @@
 #define EC_IOCTL_SC_EMERG_CLEAR        EC_IOW(0x38, ec_ioctl_sc_emerg_t)
 #define EC_IOCTL_SC_EMERG_OVERRUNS    EC_IOWR(0x39, ec_ioctl_sc_emerg_t)
 #define EC_IOCTL_SC_SDO_REQUEST       EC_IOWR(0x3a, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SC_VOE               EC_IOWR(0x3b, ec_ioctl_voe_t)
-#define EC_IOCTL_SC_STATE             EC_IOWR(0x3c, ec_ioctl_sc_state_t)
-#define EC_IOCTL_SC_IDN                EC_IOW(0x3d, ec_ioctl_sc_idn_t)
-#define EC_IOCTL_DOMAIN_OFFSET          EC_IO(0x3e)
-#define EC_IOCTL_DOMAIN_PROCESS         EC_IO(0x3f)
-#define EC_IOCTL_DOMAIN_QUEUE           EC_IO(0x40)
-#define EC_IOCTL_DOMAIN_STATE         EC_IOWR(0x41, ec_ioctl_domain_state_t)
-#define EC_IOCTL_SDO_REQUEST_INDEX    EC_IOWR(0x42, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_TIMEOUT  EC_IOWR(0x43, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_STATE    EC_IOWR(0x44, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_READ     EC_IOWR(0x45, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_WRITE    EC_IOWR(0x46, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_DATA     EC_IOWR(0x47, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_VOE_SEND_HEADER       EC_IOW(0x48, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_REC_HEADER       EC_IOWR(0x49, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ              EC_IOW(0x4a, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ_NOSYNC       EC_IOW(0x4b, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_WRITE            EC_IOWR(0x4c, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_EXEC             EC_IOWR(0x4d, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_DATA             EC_IOWR(0x4e, ec_ioctl_voe_t)
-#define EC_IOCTL_SET_SEND_INTERVAL     EC_IOW(0x4f, size_t)
+#define EC_IOCTL_SC_REG_REQUEST       EC_IOWR(0x3b, ec_ioctl_reg_request_t)
+#define EC_IOCTL_SC_VOE               EC_IOWR(0x3c, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_STATE             EC_IOWR(0x3d, ec_ioctl_sc_state_t)
+#define EC_IOCTL_SC_IDN                EC_IOW(0x3e, ec_ioctl_sc_idn_t)
+#define EC_IOCTL_DOMAIN_OFFSET          EC_IO(0x3f)
+#define EC_IOCTL_DOMAIN_PROCESS         EC_IO(0x40)
+#define EC_IOCTL_DOMAIN_QUEUE           EC_IO(0x41)
+#define EC_IOCTL_DOMAIN_STATE         EC_IOWR(0x42, ec_ioctl_domain_state_t)
+#define EC_IOCTL_SDO_REQUEST_INDEX    EC_IOWR(0x43, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_TIMEOUT  EC_IOWR(0x44, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_STATE    EC_IOWR(0x45, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_READ     EC_IOWR(0x46, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_WRITE    EC_IOWR(0x47, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_DATA     EC_IOWR(0x48, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_REG_REQUEST_DATA     EC_IOWR(0x49, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_STATE    EC_IOWR(0x4a, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_WRITE    EC_IOWR(0x4b, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_READ     EC_IOWR(0x4c, ec_ioctl_reg_request_t)
+#define EC_IOCTL_VOE_SEND_HEADER       EC_IOW(0x4d, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_REC_HEADER       EC_IOWR(0x4e, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ              EC_IOW(0x4f, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ_NOSYNC       EC_IOW(0x50, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_WRITE            EC_IOWR(0x51, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_EXEC             EC_IOWR(0x52, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_DATA             EC_IOWR(0x53, ec_ioctl_voe_t)
+#define EC_IOCTL_SET_SEND_INTERVAL     EC_IOW(0x54, size_t)
 
 /*****************************************************************************/
 
@@ -409,8 +414,8 @@
 typedef struct {
     // inputs
     uint16_t slave_position;
-    uint16_t offset;
-    uint16_t length;
+    uint16_t address;
+    size_t size;
     uint8_t *data;
 } ec_ioctl_slave_reg_t;
 
@@ -686,6 +691,22 @@
 typedef struct {
     // inputs
     uint32_t config_index;
+    size_t mem_size;
+
+    // inputs/outputs
+    uint32_t request_index;
+    uint8_t *data;
+    ec_request_state_t state;
+    uint8_t new_data;
+    uint16_t address;
+    size_t transfer_size;
+} ec_ioctl_reg_request_t;
+
+/*****************************************************************************/
+
+typedef struct {
+    // inputs
+    uint32_t config_index;
 
     // inputs/outputs
     uint32_t voe_index;
--- a/master/master.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/master.c	Wed Nov 14 22:12:57 2012 +0100
@@ -208,9 +208,6 @@
     INIT_LIST_HEAD(&master->sii_requests);
     init_waitqueue_head(&master->sii_queue);
 
-    INIT_LIST_HEAD(&master->reg_requests);
-    init_waitqueue_head(&master->reg_queue);
-
     // init devices
     ret = ec_device_init(&master->devices[EC_DEVICE_MAIN], master);
     if (ret < 0)
@@ -429,16 +426,6 @@
         wake_up(&master->sii_queue);
     }
 
-    while (!list_empty(&master->reg_requests)) {
-        ec_reg_request_t *request =
-            list_entry(master->reg_requests.next, ec_reg_request_t, list);
-        list_del_init(&request->list); // dequeue
-        EC_MASTER_WARN(master, "Discarding register request, slave %u"
-                " about to be deleted.\n", request->slave->ring_position);
-        request->state = EC_INT_REQUEST_FAILURE;
-        wake_up(&master->reg_queue);
-    }
-
     for (slave = master->slaves;
             slave < master->slaves + master->slave_count;
             slave++) {
@@ -874,8 +861,8 @@
     list_for_each_entry(queued_datagram, &master->datagram_queue, queue) {
         if (queued_datagram == datagram) {
             datagram->skip_count++;
-            EC_MASTER_DBG(master, 1, "Skipping re-initialized datagram %p.\n",
-                    datagram);
+            EC_MASTER_DBG(master, 1,
+                    "Datagram %p already queued (skipping).\n", datagram);
             datagram->state = EC_DATAGRAM_QUEUED;
             return;
         }
--- a/master/master.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/master.h	Wed Nov 14 22:12:57 2012 +0100
@@ -276,9 +276,6 @@
     struct list_head sii_requests; /**< SII write requests. */
     wait_queue_head_t sii_queue; /**< Wait queue for SII
                                       write requests from user space. */
-
-    struct list_head reg_requests; /**< Register requests. */
-    wait_queue_head_t reg_queue; /**< Wait queue for register requests. */
 };
 
 /*****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/reg_request.c	Wed Nov 14 22:12:57 2012 +0100
@@ -0,0 +1,132 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2012  Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ *  This file is part of the IgH EtherCAT Master.
+ *
+ *  The IgH EtherCAT Master is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License version 2, as
+ *  published by the Free Software Foundation.
+ *
+ *  The IgH EtherCAT Master is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+ *  Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with the IgH EtherCAT Master; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  ---
+ *
+ *  The license mentioned above concerns the source code only. Using the
+ *  EtherCAT technology and brand is only permitted in compliance with the
+ *  industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/** \file
+ * Register request functions.
+ */
+
+/*****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/jiffies.h>
+#include <linux/slab.h>
+
+#include "reg_request.h"
+
+/*****************************************************************************/
+
+void ec_reg_request_clear_data(ec_reg_request_t *);
+
+/*****************************************************************************/
+
+/** Register request constructor.
+ */
+int ec_reg_request_init(
+        ec_reg_request_t *reg, /**< Register request. */
+        size_t size /**< Memory size. */
+        )
+{
+    if (!(reg->data = (uint8_t *) kmalloc(size, GFP_KERNEL))) {
+        EC_ERR("Failed to allocate %zu bytes of register memory.\n", size);
+        return -ENOMEM;
+    }
+
+    INIT_LIST_HEAD(&reg->list);
+    reg->mem_size = size;
+    memset(reg->data, 0x00, size);
+    reg->dir = EC_DIR_INVALID;
+    reg->address = 0;
+    reg->transfer_size = 0;
+    reg->state = EC_INT_REQUEST_INIT;
+    return 0;
+}
+
+/*****************************************************************************/
+
+/** Register request destructor.
+ */
+void ec_reg_request_clear(
+        ec_reg_request_t *reg /**< Register request. */
+        )
+{
+    if (reg->data) {
+        kfree(reg->data);
+    }
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+uint8_t *ecrt_reg_request_data(ec_reg_request_t *reg)
+{
+    return reg->data;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_reg_request_state(const ec_reg_request_t *reg)
+{
+   return ec_request_state_translation_table[reg->state];
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_write(ec_reg_request_t *reg, uint16_t address,
+        size_t size)
+{
+    reg->dir = EC_DIR_OUTPUT;
+    reg->address = address;
+    reg->transfer_size = min(size, reg->mem_size);
+    reg->state = EC_INT_REQUEST_QUEUED;
+}
+
+/*****************************************************************************/
+
+void ecrt_reg_request_read(ec_reg_request_t *reg, uint16_t address,
+        size_t size)
+{
+    reg->dir = EC_DIR_INPUT;
+    reg->address = address;
+    reg->transfer_size = min(size, reg->mem_size);
+    reg->state = EC_INT_REQUEST_QUEUED;
+}
+
+/*****************************************************************************/
+
+/** \cond */
+
+EXPORT_SYMBOL(ecrt_reg_request_data);
+EXPORT_SYMBOL(ecrt_reg_request_state);
+EXPORT_SYMBOL(ecrt_reg_request_write);
+EXPORT_SYMBOL(ecrt_reg_request_read);
+
+/** \endcond */
+
+/*****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/master/reg_request.h	Wed Nov 14 22:12:57 2012 +0100
@@ -0,0 +1,66 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2012  Florian Pose, Ingenieurgemeinschaft IgH
+ *
+ *  This file is part of the IgH EtherCAT Master.
+ *
+ *  The IgH EtherCAT Master is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License version 2, as
+ *  published by the Free Software Foundation.
+ *
+ *  The IgH EtherCAT Master is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
+ *  Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with the IgH EtherCAT Master; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ *  ---
+ *
+ *  The license mentioned above concerns the source code only. Using the
+ *  EtherCAT technology and brand is only permitted in compliance with the
+ *  industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/**
+   \file
+   EtherCAT register request structure.
+*/
+
+/*****************************************************************************/
+
+#ifndef __EC_REG_REQUEST_H__
+#define __EC_REG_REQUEST_H__
+
+#include <linux/list.h>
+
+#include "globals.h"
+
+/*****************************************************************************/
+
+/** Register request.
+ */
+struct ec_reg_request {
+    struct list_head list; /**< List item. */
+    size_t mem_size; /**< Size of data memory. */
+    uint8_t *data; /**< Pointer to data memory. */
+    ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means writing to the
+                          slave, EC_DIR_INPUT means reading from the slave. */
+    uint16_t address; /**< Register address. */
+    size_t transfer_size; /*< Size of the data to transfer. */
+    ec_internal_request_state_t state; /**< Request state. */
+};
+
+/*****************************************************************************/
+
+int ec_reg_request_init(ec_reg_request_t *, size_t);
+void ec_reg_request_clear(ec_reg_request_t *);
+
+/*****************************************************************************/
+
+#endif
--- a/master/slave.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/slave.c	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -155,6 +155,9 @@
     INIT_LIST_HEAD(&slave->slave_sdo_requests);
     init_waitqueue_head(&slave->sdo_queue);
 
+    INIT_LIST_HEAD(&slave->reg_requests);
+    init_waitqueue_head(&slave->reg_queue);
+
     INIT_LIST_HEAD(&slave->foe_requests);
     init_waitqueue_head(&slave->foe_queue);
 
@@ -202,6 +205,16 @@
         wake_up(&slave->sdo_queue);
     }
 
+    while (!list_empty(&slave->reg_requests)) {
+        ec_reg_request_t *reg =
+            list_entry(slave->reg_requests.next, ec_reg_request_t, list);
+        list_del_init(&reg->list); // dequeue
+        EC_SLAVE_WARN(slave, "Discarding register request,"
+                " slave about to be deleted.\n");
+        reg->state = EC_INT_REQUEST_FAILURE;
+        wake_up(&slave->reg_queue);
+    }
+
     while (!list_empty(&slave->foe_requests)) {
         ec_master_foe_request_t *request =
             list_entry(slave->foe_requests.next,
@@ -224,8 +237,9 @@
         wake_up(&slave->soe_queue);
     }
 
-    if (slave->config)
+    if (slave->config) {
         ec_slave_config_detach(slave->config);
+    }
 
     // free all SDOs
     list_for_each_entry_safe(sdo, next_sdo, &slave->sdo_dictionary, list) {
@@ -251,8 +265,10 @@
         kfree(pdo);
     }
 
-    if (slave->sii_words)
+    if (slave->sii_words) {
         kfree(slave->sii_words);
+    }
+
     ec_fsm_slave_clear(&slave->fsm);
     ec_datagram_clear(&slave->fsm_datagram);
 }
--- a/master/slave.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/slave.h	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -227,6 +227,9 @@
     struct list_head slave_sdo_requests; /**< SDO access requests. */
     wait_queue_head_t sdo_queue; /**< Wait queue for SDO access requests
                                    from user space. */
+    struct list_head reg_requests; /**< Register access requests. */
+    wait_queue_head_t reg_queue; /**< Wait queue for register access requests
+                                   from user space. */
     struct list_head foe_requests; /**< FoE write requests. */
     wait_queue_head_t foe_queue; /**< Wait queue for FoE requests from user
                                    space. */
--- a/master/slave_config.c	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/slave_config.c	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -86,6 +86,7 @@
 
     INIT_LIST_HEAD(&sc->sdo_configs);
     INIT_LIST_HEAD(&sc->sdo_requests);
+    INIT_LIST_HEAD(&sc->reg_requests);
     INIT_LIST_HEAD(&sc->voe_handlers);
     INIT_LIST_HEAD(&sc->soe_configs);
 
@@ -105,6 +106,7 @@
     unsigned int i;
     ec_sdo_request_t *req, *next_req;
     ec_voe_handler_t *voe, *next_voe;
+    ec_reg_request_t *reg, *next_reg;
     ec_soe_request_t *soe, *next_soe;
 
     ec_slave_config_detach(sc);
@@ -127,6 +129,13 @@
         kfree(req);
     }
 
+    // free all register requests
+    list_for_each_entry_safe(reg, next_reg, &sc->reg_requests, list) {
+        list_del(&reg->list);
+        ec_reg_request_clear(reg);
+        kfree(reg);
+    }
+
     // free all VoE handlers
     list_for_each_entry_safe(voe, next_voe, &sc->voe_handlers, list) {
         list_del(&voe->list);
@@ -429,7 +438,7 @@
 
 /*****************************************************************************/
 
-/** Finds a VoE handler via its position in the list.
+/** Finds a CoE handler via its position in the list.
  */
 ec_sdo_request_t *ec_slave_config_find_sdo_request(
         ec_slave_config_t *sc, /**< Slave configuration. */
@@ -449,6 +458,26 @@
 
 /*****************************************************************************/
 
+/** Finds a register handler via its position in the list.
+ */
+ec_reg_request_t *ec_slave_config_find_reg_request(
+        ec_slave_config_t *sc, /**< Slave configuration. */
+        unsigned int pos /**< Position in the list. */
+        )
+{
+    ec_reg_request_t *reg;
+
+    list_for_each_entry(reg, &sc->reg_requests, list) {
+        if (pos--)
+            continue;
+        return reg;
+    }
+
+    return NULL;
+}
+
+/*****************************************************************************/
+
 /** Finds a VoE handler via its position in the list.
  */
 ec_voe_handler_t *ec_slave_config_find_voe_handler(
@@ -965,6 +994,49 @@
 
 /*****************************************************************************/
 
+/** Same as ecrt_slave_config_create_reg_request(), but with ERR_PTR() return
+ * value.
+ */
+ec_reg_request_t *ecrt_slave_config_create_reg_request_err(
+        ec_slave_config_t *sc, size_t size)
+{
+    ec_reg_request_t *reg;
+    int ret;
+
+    EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, size = %zu)\n",
+            __func__, sc, size);
+
+    if (!(reg = (ec_reg_request_t *)
+                kmalloc(sizeof(ec_reg_request_t), GFP_KERNEL))) {
+        EC_CONFIG_ERR(sc, "Failed to allocate register request memory!\n");
+        return ERR_PTR(-ENOMEM);
+    }
+
+    ret = ec_reg_request_init(reg, size);
+    if (ret) {
+        kfree(reg);
+        return ERR_PTR(ret);
+    }
+
+    down(&sc->master->master_sem);
+    list_add_tail(&reg->list, &sc->reg_requests);
+    up(&sc->master->master_sem);
+
+    return reg;
+}
+
+/*****************************************************************************/
+
+ec_reg_request_t *ecrt_slave_config_create_reg_request(
+        ec_slave_config_t *sc, size_t size)
+{
+    ec_reg_request_t *reg =
+        ecrt_slave_config_create_reg_request_err(sc, size);
+    return IS_ERR(reg) ? NULL : reg;
+}
+
+/*****************************************************************************/
+
 /** Same as ecrt_slave_config_create_voe_handler(), but with ERR_PTR() return
  * value.
  */
@@ -1101,6 +1173,7 @@
 EXPORT_SYMBOL(ecrt_slave_config_emerg_overruns);
 EXPORT_SYMBOL(ecrt_slave_config_create_sdo_request);
 EXPORT_SYMBOL(ecrt_slave_config_create_voe_handler);
+EXPORT_SYMBOL(ecrt_slave_config_create_reg_request);
 EXPORT_SYMBOL(ecrt_slave_config_state);
 EXPORT_SYMBOL(ecrt_slave_config_idn);
 
--- a/master/slave_config.h	Wed Nov 14 22:08:32 2012 +0100
+++ b/master/slave_config.h	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -141,6 +141,7 @@
     struct list_head sdo_configs; /**< List of SDO configurations. */
     struct list_head sdo_requests; /**< List of SDO requests. */
     struct list_head voe_handlers; /**< List of VoE handlers. */
+    struct list_head reg_requests; /**< List of register requests. */
     struct list_head soe_configs; /**< List of SoE configurations. */
 
     ec_coe_emerg_ring_t emerg_ring; /**< CoE emergency ring buffer. */
@@ -165,6 +166,8 @@
         const ec_slave_config_t *, unsigned int);
 ec_sdo_request_t *ec_slave_config_find_sdo_request(ec_slave_config_t *,
         unsigned int);
+ec_reg_request_t *ec_slave_config_find_reg_request(ec_slave_config_t *,
+        unsigned int);
 ec_voe_handler_t *ec_slave_config_find_voe_handler(ec_slave_config_t *,
         unsigned int);
 
@@ -172,6 +175,8 @@
         ec_slave_config_t *, uint16_t, uint8_t, size_t);
 ec_voe_handler_t *ecrt_slave_config_create_voe_handler_err(
         ec_slave_config_t *, size_t);
+ec_reg_request_t *ecrt_slave_config_create_reg_request_err(
+        ec_slave_config_t *, size_t);
 
 /*****************************************************************************/
 
--- a/tool/CommandRegRead.cpp	Wed Nov 14 22:08:32 2012 +0100
+++ b/tool/CommandRegRead.cpp	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -48,20 +48,20 @@
     stringstream str;
 
     str << binaryBaseName << " " << getName()
-        << " [OPTIONS] <OFFSET> [LENGTH]" << endl
+        << " [OPTIONS] <ADDRESS> [SIZE]" << endl
         << endl
         << getBriefDescription() << endl
         << endl
         << "This command requires a single slave to be selected." << endl
         << endl
         << "Arguments:" << endl
-        << "  OFFSET is the register address. Must" << endl
-        << "         be an unsigned 16 bit number." << endl
-        << "  LENGTH is the number of bytes to read and must also be" << endl
-        << "         an unsigned 16 bit number. OFFSET plus LENGTH" << endl
-        << "         may not exceed 64k. The length is ignored (and" << endl
-        << "         can be omitted), if a selected data type" << endl
-        << "         implies a length." << endl
+        << "  ADDRESS is the register address. Must" << endl
+        << "          be an unsigned 16 bit number." << endl
+        << "  SIZE    is the number of bytes to read and must also be" << endl
+        << "          an unsigned 16 bit number. ADDRESS plus SIZE" << endl
+        << "          may not exceed 64k. The size is ignored (and" << endl
+        << "          can be omitted), if a selected data type" << endl
+        << "          implies a size." << endl
         << endl
         << typeInfo()
         << endl
@@ -81,7 +81,7 @@
 void CommandRegRead::execute(const StringVector &args)
 {
     SlaveList slaves;
-    ec_ioctl_slave_reg_t data;
+    ec_ioctl_slave_reg_t io;
     stringstream strOffset, err;
     const DataType *dataType = NULL;
 
@@ -93,9 +93,9 @@
     strOffset << args[0];
     strOffset
         >> resetiosflags(ios::basefield) // guess base from prefix
-        >> data.offset;
+        >> io.address;
     if (strOffset.fail()) {
-        err << "Invalid offset '" << args[0] << "'!";
+        err << "Invalid address '" << args[0] << "'!";
         throwInvalidUsageException(err);
     }
 
@@ -104,18 +104,18 @@
         strLength << args[1];
         strLength
             >> resetiosflags(ios::basefield) // guess base from prefix
-            >> data.length;
+            >> io.size;
         if (strLength.fail()) {
-            err << "Invalid length '" << args[1] << "'!";
+            err << "Invalid size '" << args[1] << "'!";
             throwInvalidUsageException(err);
         }
 
-        if (!data.length) {
+        if (!io.size) {
             err << "Length may not be zero!";
             throwInvalidUsageException(err);
         }
-    } else { // no length argument given
-        data.length = 0;
+    } else { // no size argument given
+        io.size = 0;
     }
 
     if (!getDataType().empty()) {
@@ -125,19 +125,19 @@
         }
 
         if (dataType->byteSize) {
-            // override length argument
-            data.length = dataType->byteSize;
+            // override size argument
+            io.size = dataType->byteSize;
         }
     }
 
-    if (!data.length) {
-        err << "The length argument is mandatory, if no datatype is " << endl
-            << "specified, or the datatype does not imply a length!";
+    if (!io.size) {
+        err << "The size argument is mandatory, if no datatype is " << endl
+            << "specified, or the datatype does not imply a size!";
         throwInvalidUsageException(err);
     }
 
-    if ((uint32_t) data.offset + data.length > 0xffff) {
-        err << "Offset and length exceeding 64k!";
+    if ((uint32_t) io.address + io.size > 0xffff) {
+        err << "Address and size exceeding 64k!";
         throwInvalidUsageException(err);
     }
 
@@ -148,25 +148,25 @@
     if (slaves.size() != 1) {
         throwSingleSlaveRequired(slaves.size());
     }
-    data.slave_position = slaves.front().position;
+    io.slave_position = slaves.front().position;
 
-    data.data = new uint8_t[data.length];
+    io.data = new uint8_t[io.size];
 
     try {
-        m.readReg(&data);
+        m.readReg(&io);
     } catch (MasterDeviceException &e) {
-        delete [] data.data;
+        delete [] io.data;
         throw e;
     }
 
     try {
-        outputData(cout, dataType, data.data, data.length);
+        outputData(cout, dataType, io.data, io.size);
     } catch (SizeException &e) {
-        delete [] data.data;
+        delete [] io.data;
         throwCommandException(e.what());
     }
 
-    delete [] data.data;
+    delete [] io.data;
 }
 
 /*****************************************************************************/
--- a/tool/CommandRegWrite.cpp	Wed Nov 14 22:08:32 2012 +0100
+++ b/tool/CommandRegWrite.cpp	Wed Nov 14 22:12:57 2012 +0100
@@ -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.
  *
@@ -57,7 +57,7 @@
         << "This command requires a single slave to be selected." << endl
         << endl
         << "Arguments:" << endl
-        << "  OFFSET  is the register address to write to." << endl
+        << "  ADDRESS is the register address to write to." << endl
         << "  DATA    depends on whether a datatype was specified" << endl
         << "          with the --type option: If not, DATA must be" << endl
         << "          either a path to a file with data to write," << endl
@@ -83,7 +83,7 @@
 void CommandRegWrite::execute(const StringVector &args)
 {
     stringstream strOffset, err;
-    ec_ioctl_slave_reg_t data;
+    ec_ioctl_slave_reg_t io;
     ifstream file;
     SlaveList slaves;
 
@@ -95,22 +95,22 @@
     strOffset << args[0];
     strOffset
         >> resetiosflags(ios::basefield) // guess base from prefix
-        >> data.offset;
+        >> io.address;
     if (strOffset.fail()) {
-        err << "Invalid offset '" << args[0] << "'!";
+        err << "Invalid address '" << args[0] << "'!";
         throwInvalidUsageException(err);
     }
 
     if (getDataType().empty()) {
         if (args[1] == "-") {
-            loadRegData(&data, cin);
+            loadRegData(&io, cin);
         } else {
             file.open(args[1].c_str(), ifstream::in | ifstream::binary);
             if (file.fail()) {
                 err << "Failed to open '" << args[1] << "'!";
                 throwCommandException(err);
             }
-            loadRegData(&data, file);
+            loadRegData(&io, file);
             file.close();
         }
     } else {
@@ -123,30 +123,30 @@
         }
 
         if (dataType->byteSize) {
-            data.length = dataType->byteSize;
+            io.size = dataType->byteSize;
         } else {
-            data.length = 1024; // FIXME
-        }
-
-        data.data = new uint8_t[data.length];
+            io.size = 1024; // FIXME
+        }
+
+        io.data = new uint8_t[io.size];
 
         try {
-            data.length = interpretAsType(
-                    dataType, args[1], data.data, data.length);
+            io.size = interpretAsType(
+                    dataType, args[1], io.data, io.size);
         } catch (SizeException &e) {
-            delete [] data.data;
+            delete [] io.data;
             throwCommandException(e.what());
         } catch (ios::failure &e) {
-            delete [] data.data;
+            delete [] io.data;
             err << "Invalid value argument '" << args[1]
                 << "' for type '" << dataType->name << "'!";
             throwInvalidUsageException(err);
         }
     }
 
-    if ((uint32_t) data.offset + data.length > 0xffff) {
-        err << "Offset and length exceeding 64k!";
-        delete [] data.data;
+    if ((uint32_t) io.address + io.size > 0xffff) {
+        err << "Address and size exceeding 64k!";
+        delete [] io.data;
         throwInvalidUsageException(err);
     }
 
@@ -154,22 +154,22 @@
     try {
         m.open(MasterDevice::ReadWrite);
     } catch (MasterDeviceException &e) {
-        delete [] data.data;
+        delete [] io.data;
         throw e;
     }
 
     slaves = selectedSlaves(m);
     if (slaves.size() != 1) {
-        delete [] data.data;
+        delete [] io.data;
         throwSingleSlaveRequired(slaves.size());
     }
-    data.slave_position = slaves.front().position;
+    io.slave_position = slaves.front().position;
 
     // send data to master
     try {
-        m.writeReg(&data);
+        m.writeReg(&io);
     } catch (MasterDeviceException &e) {
-        delete [] data.data;
+        delete [] io.data;
         throw e;
     }
 
@@ -177,13 +177,13 @@
         cerr << "Register writing finished." << endl;
     }
 
-    delete [] data.data;
+    delete [] io.data;
 }
 
 /*****************************************************************************/
 
 void CommandRegWrite::loadRegData(
-        ec_ioctl_slave_reg_t *data,
+        ec_ioctl_slave_reg_t *io,
         const istream &in
         )
 {
@@ -201,11 +201,11 @@
         err << "Invalid data size " << contents.size() << "!";
         throwInvalidUsageException(err);
     }
-    data->length = contents.size();
+    io->size = contents.size();
 
     // allocate buffer and read file into buffer
-    data->data = new uint8_t[data->length];
-    contents.copy((char *) data->data, contents.size());
-}
-
-/*****************************************************************************/
+    io->data = new uint8_t[io->size];
+    contents.copy((char *) io->data, contents.size());
+}
+
+/*****************************************************************************/