Implemented SDO requests in userspace library.
authorFlorian Pose <fp@igh-essen.com>
Mon, 02 Feb 2009 13:37:00 +0000
changeset 1352 275d2fdeab48
parent 1351 cf8f08631c8e
child 1353 22b1de4c74e4
Implemented SDO requests in userspace library.
TODO
examples/user/main.c
include/ecrt.h
lib/Makefile.am
lib/sdo_request.c
lib/sdo_request.h
lib/slave_config.c
master/cdev.c
master/ioctl.h
--- a/TODO	Mon Feb 02 13:34:01 2009 +0000
+++ b/TODO	Mon Feb 02 13:37:00 2009 +0000
@@ -12,11 +12,10 @@
 
 * Remove ecrt_domain_state().
 * Segmented SDO downloads.
-* Implement SDO handlers in userspace library.
 * Clear slave list on link down.
 * Check force_config flag before error.
 * Remove allow_scanning flag.
-* File access over EtherCAT (FoE).
+* Test File access over EtherCAT (FoE) reading.
 * Improve application-triggered SDO transfers by moving the statemachine into
   the SDO handlers.
 * External memory for SDO transfers.
--- a/examples/user/main.c	Mon Feb 02 13:34:01 2009 +0000
+++ b/examples/user/main.c	Mon Feb 02 13:37:00 2009 +0000
@@ -45,6 +45,7 @@
 
 // Optional features
 #define CONFIGURE_PDOS  1
+#define SDO_ACCESS      0
 
 /****************************************************************************/
 
@@ -68,14 +69,15 @@
 static uint8_t *domain1_pd = NULL;
 
 #define BusCouplerPos  0, 0
-#define AnaOutSlavePos 0, 1
-#define AnaInSlavePos  0, 2
-#define DigOutSlavePos 0, 3
+#define DigOutSlavePos 0, 2
+#define AnaInSlavePos  0, 3
+#define AnaOutSlavePos 0, 4
 
 #define Beckhoff_EK1100 0x00000002, 0x044c2c52
 #define Beckhoff_EL2004 0x00000002, 0x07d43052
 #define Beckhoff_EL2032 0x00000002, 0x07f03052
 #define Beckhoff_EL3152 0x00000002, 0x0c503052
+#define Beckhoff_EL3102 0x00000002, 0x0c1e3052
 #define Beckhoff_EL4102 0x00000002, 0x10063052
 
 // offsets for PDO entries
@@ -85,8 +87,8 @@
 static unsigned int off_dig_out;
 
 const static ec_pdo_entry_reg_t domain1_regs[] = {
-    {AnaInSlavePos,  Beckhoff_EL3152, 0x3101, 1, &off_ana_in_status},
-    {AnaInSlavePos,  Beckhoff_EL3152, 0x3101, 2, &off_ana_in_value},
+    {AnaInSlavePos,  Beckhoff_EL3102, 0x3101, 1, &off_ana_in_status},
+    {AnaInSlavePos,  Beckhoff_EL3102, 0x3101, 2, &off_ana_in_value},
 	{AnaOutSlavePos, Beckhoff_EL4102, 0x3001, 1, &off_ana_out},
 	{DigOutSlavePos, Beckhoff_EL2032, 0x3001, 1, &off_dig_out},
 	{}
@@ -101,7 +103,7 @@
 
 // Analog in --------------------------
 
-static ec_pdo_entry_info_t el3152_pdo_entries[] = {
+static ec_pdo_entry_info_t el3102_pdo_entries[] = {
     {0x3101, 1,  8}, // channel 1 status
     {0x3101, 2, 16}, // channel 1 value
     {0x3102, 1,  8}, // channel 2 status
@@ -110,14 +112,14 @@
     {0x6401, 2, 16}  // channel 2 value (alt.)
 };
 
-static ec_pdo_info_t el3152_pdos[] = {
-    {0x1A00, 2, el3152_pdo_entries},
-    {0x1A01, 2, el3152_pdo_entries + 2}
-};
-
-static ec_sync_info_t el3152_syncs[] = {
+static ec_pdo_info_t el3102_pdos[] = {
+    {0x1A00, 2, el3102_pdo_entries},
+    {0x1A01, 2, el3102_pdo_entries + 2}
+};
+
+static ec_sync_info_t el3102_syncs[] = {
     {2, EC_DIR_OUTPUT},
-    {3, EC_DIR_INPUT, 2, el3152_pdos},
+    {3, EC_DIR_INPUT, 2, el3102_pdos},
     {0xff}
 };
 
@@ -164,6 +166,12 @@
 
 /*****************************************************************************/
 
+#if SDO_ACCESS
+static ec_sdo_request_t *sdo;
+#endif
+
+/*****************************************************************************/
+
 void check_domain1_state(void)
 {
     ec_domain_state_t ds;
@@ -215,6 +223,31 @@
     sc_ana_in_state = s;
 }
 
+/*****************************************************************************/
+
+#if SDO_ACCESS
+void read_sdo(void)
+{
+    switch (ecrt_sdo_request_state(sdo)) {
+        case EC_REQUEST_UNUSED: // request was not used yet
+            ecrt_sdo_request_read(sdo); // trigger first read
+            break;
+        case EC_REQUEST_BUSY:
+            fprintf(stderr, "Still busy...\n");
+            break;
+        case EC_REQUEST_SUCCESS:
+            fprintf(stderr, "SDO value: 0x%04X\n",
+                    EC_READ_U16(ecrt_sdo_request_data(sdo)));
+            ecrt_sdo_request_read(sdo); // trigger next read
+            break;
+        case EC_REQUEST_ERROR:
+            fprintf(stderr, "Failed to read SDO!\n");
+            ecrt_sdo_request_read(sdo); // retry reading
+            break;
+    }
+}
+#endif
+
 /****************************************************************************/
 
 void cyclic_task()
@@ -241,6 +274,12 @@
 
         // check for islave configuration state(s) (optional)
         check_slave_config_states();
+
+#if SDO_ACCESS
+        // read process data SDO
+        read_sdo();
+#endif
+
     }
 
 #if 0
@@ -287,14 +326,23 @@
         return -1;
 
     if (!(sc_ana_in = ecrt_master_slave_config(
-                    master, AnaInSlavePos, Beckhoff_EL3152))) {
+                    master, AnaInSlavePos, Beckhoff_EL3102))) {
         fprintf(stderr, "Failed to get slave configuration.\n");
         return -1;
     }
 
+#if SDO_ACCESS
+    fprintf(stderr, "Creating SDO requests...\n");
+    if (!(sdo = ecrt_slave_config_create_sdo_request(sc_ana_in, 0x3102, 2, 2))) {
+        fprintf(stderr, "Failed to create SDO request.\n");
+        return -1;
+    }
+    ecrt_sdo_request_timeout(sdo, 500); // ms
+#endif
+
 #if CONFIGURE_PDOS
     printf("Configuring PDOs...\n");
-    if (ecrt_slave_config_pdos(sc_ana_in, EC_END, el3152_syncs)) {
+    if (ecrt_slave_config_pdos(sc_ana_in, EC_END, el3102_syncs)) {
         fprintf(stderr, "Failed to configure PDOs.\n");
         return -1;
     }
--- a/include/ecrt.h	Mon Feb 02 13:34:01 2009 +0000
+++ b/include/ecrt.h	Mon Feb 02 13:37:00 2009 +0000
@@ -45,6 +45,8 @@
  * - Renamed ec_sdo_request_state_t to ec_request_state_t, because it is also
  *   used by VoE handlers.
  * - Added ecrt_master_slave() to get information about a certain slave.
+ * - Removed 'const' from argument of ecrt_sdo_request_state(), because the
+ *   userspace library has to modify object internals.
  *
  * Changes in Version 1.4:
  *
@@ -969,9 +971,15 @@
  *
  * \return Request state.
  */
+#ifdef __KERNEL__
 ec_request_state_t ecrt_sdo_request_state(
-    const ec_sdo_request_t *req /**< SDO request. */
+        const ec_sdo_request_t *req /**< SDO request. */
     );
+#else
+ec_request_state_t ecrt_sdo_request_state(
+        ec_sdo_request_t *req /**< SDO request. */
+    );
+#endif
 
 /** Schedule an SDO write operation.
  *
--- a/lib/Makefile.am	Mon Feb 02 13:34:01 2009 +0000
+++ b/lib/Makefile.am	Mon Feb 02 13:37:00 2009 +0000
@@ -37,12 +37,14 @@
 	common.c \
 	domain.c \
 	master.c \
+	sdo_request.c \
 	slave_config.c \
 	voe_handler.c
 
 noinst_HEADERS = \
 	domain.h \
 	master.h \
+	sdo_request.h \
 	slave_config.h \
 	voe_handler.h
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/sdo_request.c	Mon Feb 02 13:37:00 2009 +0000
@@ -0,0 +1,147 @@
+/******************************************************************************
+ *
+ *  $Id$
+ *
+ *  Copyright (C) 2006-2009  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
+ *
+ *  Using the EtherCAT technology and brand is permitted in compliance with
+ *  the industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/** \file
+ * Canopen over EtherCAT SDO request functions.
+ */
+
+/*****************************************************************************/
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "sdo_request.h"
+#include "master/ioctl.h"
+#include "slave_config.h"
+#include "master.h"
+
+/*****************************************************************************
+ * Realtime interface.
+ ****************************************************************************/
+
+void ecrt_sdo_request_timeout(ec_sdo_request_t *req, uint32_t timeout)
+{
+    ec_ioctl_sdo_request_t data;
+
+    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)
+        fprintf(stderr, "Failed to set SDO request timeout: %s\n",
+                strerror(errno));
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_sdo_request_data(ec_sdo_request_t *req)
+{
+    return req->data;
+}
+
+/*****************************************************************************/
+
+size_t ecrt_sdo_request_data_size(const ec_sdo_request_t *req)
+{
+    return req->data_size;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_sdo_request_state(ec_sdo_request_t *req)
+{
+    ec_ioctl_sdo_request_t data;
+
+    data.config_index = req->config->index;
+    data.request_index = req->index;
+
+    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_STATE,
+                &data) == -1)
+        fprintf(stderr, "Failed to get SDO request state: %s\n",
+                strerror(errno));
+
+    if (data.size) { // new data waiting to be copied
+        if (req->mem_size < data.size) {
+            if (req->data)
+                free(req->data);
+            req->data = malloc(data.size);
+            if (!req->data) {
+                req->mem_size = 0;
+                fprintf(stderr, "Failed to allocate %u bytes of SDO data"
+                        " memory!\n", data.size);
+                return EC_REQUEST_ERROR;
+            }
+            req->mem_size = data.size;
+        }
+
+        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));
+            return EC_REQUEST_ERROR;
+        }
+        req->data_size = data.size;
+    }
+
+    return data.state;
+}
+
+/*****************************************************************************/
+
+void ecrt_sdo_request_read(ec_sdo_request_t *req)
+{
+    ec_ioctl_sdo_request_t data;
+
+    data.config_index = req->config->index;
+    data.request_index = req->index;
+
+    if (ioctl(req->config->master->fd, EC_IOCTL_SDO_REQUEST_READ,
+                &data) == -1)
+        fprintf(stderr, "Failed to command an SDO read operation : %s\n",
+                strerror(errno));
+}
+
+/*****************************************************************************/
+
+void ecrt_sdo_request_write(ec_sdo_request_t *req)
+{
+    ec_ioctl_sdo_request_t data;
+
+    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)
+        fprintf(stderr, "Failed to command an SDO write operation : %s\n",
+                strerror(errno));
+}
+
+/*****************************************************************************/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib/sdo_request.h	Mon Feb 02 13:37:00 2009 +0000
@@ -0,0 +1,42 @@
+/******************************************************************************
+ *  
+ *  $Id$
+ * 
+ *  Copyright (C) 2006-2009  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/>.
+ *  
+ *  Using the EtherCAT technology and brand is permitted in compliance with
+ *  the industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+#include "include/ecrt.h"
+
+/*****************************************************************************/
+
+struct ec_sdo_request {
+    ec_slave_config_t *config; /**< Parent slave configuration. */
+    unsigned int index; /**< Request index (identifier). */
+    uint16_t sdo_index; /**< SDO index. */
+    uint8_t sdo_subindex; /**< SDO subindex. */
+    uint8_t *data; /**< Pointer to SDO data. */
+    size_t mem_size; /**< Size of SDO data memory. */
+    size_t data_size; /**< Size of SDO data. */
+};
+
+/*****************************************************************************/
--- a/lib/slave_config.c	Mon Feb 02 13:34:01 2009 +0000
+++ b/lib/slave_config.c	Mon Feb 02 13:37:00 2009 +0000
@@ -33,6 +33,7 @@
 
 #include "slave_config.h"
 #include "domain.h"
+#include "sdo_request.h"
 #include "voe_handler.h"
 #include "master.h"
 #include "master/ioctl.h"
@@ -297,7 +298,48 @@
 ec_sdo_request_t *ecrt_slave_config_create_sdo_request(ec_slave_config_t *sc,
         uint16_t index, uint8_t subindex, size_t size)
 {
-    return 0; // TODO
+    ec_ioctl_sdo_request_t data;
+    ec_sdo_request_t *req;
+
+    req = malloc(sizeof(ec_sdo_request_t));
+    if (!req) {
+        fprintf(stderr, "Failed to allocate memory.\n");
+        return 0;
+    }
+
+    if (size) {
+        req->data = malloc(size);
+        if (!req->data) {
+            fprintf(stderr, "Failed to allocate %u bytes of SDO data"
+                    " memory.\n", size);
+            free(req);
+            return 0;
+        }
+    } else {
+        req->data = NULL;
+    }
+
+    data.config_index = sc->index;
+    data.sdo_index = index;
+    data.sdo_subindex = subindex;
+    data.size = size;
+    
+    if (ioctl(sc->master->fd, EC_IOCTL_SC_SDO_REQUEST, &data) == -1) {
+        fprintf(stderr, "Failed to create SDO request: %s\n",
+                strerror(errno));
+        if (req->data)
+            free(req->data);
+        free(req);
+        return NULL; 
+    }
+
+    req->config = sc;
+    req->index = data.request_index;
+    req->sdo_index = data.sdo_index;
+    req->sdo_subindex = data.sdo_subindex;
+    req->data_size = size;
+    req->mem_size = size;
+    return req;
 }
 
 /*****************************************************************************/
--- a/master/cdev.c	Mon Feb 02 13:34:01 2009 +0000
+++ b/master/cdev.c	Mon Feb 02 13:37:00 2009 +0000
@@ -1874,6 +1874,55 @@
 
 /*****************************************************************************/
 
+/** 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);
+
+    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(
@@ -2088,6 +2137,209 @@
 
 /*****************************************************************************/
 
+/** 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;
+
+    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 (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+        up(&master->master_sem);
+        return -ENOENT;
+    }
+
+    up(&master->master_sem);
+
+    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;
+
+    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 (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+        up(&master->master_sem);
+        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;
+
+    up(&master->master_sem);
+
+    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;
+
+    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 (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+        up(&master->master_sem);
+        return -ENOENT;
+    }
+
+    up(&master->master_sem);
+
+    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;
+
+	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 (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+        up(&master->master_sem);
+        return -ENOENT;
+    }
+
+    up(&master->master_sem);
+
+    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;
+
+    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 (!(req = ec_slave_config_find_sdo_request(sc, data.request_index))) {
+        up(&master->master_sem);
+        return -ENOENT;
+    }
+
+    up(&master->master_sem);
+
+    ecrt_sdo_request_timeout(req, data.timeout);
+    return 0;
+}
+
+/*****************************************************************************/
+
 /** Sets the VoE send header.
  */
 int ec_cdev_ioctl_voe_send_header(
@@ -2646,7 +2898,7 @@
     ec_master_t *master = priv->cdev->master;
 
     if (master->debug_level)
-        EC_DBG("ioctl(filp = 0x%x, cmd = 0x%x (0x%x), arg = 0x%x)\n",
+        EC_DBG("ioctl(filp = 0x%x, cmd = 0x%08x (0x%02x), arg = 0x%x)\n",
                 (u32) filp, (u32) cmd, (u32) _IOC_NR(cmd), (u32) arg);
 
     switch (cmd) {
@@ -2764,6 +3016,10 @@
             if (!(filp->f_mode & FMODE_WRITE))
 				return -EPERM;
 			return ec_cdev_ioctl_sc_sdo(master, arg, priv);
+        case EC_IOCTL_SC_SDO_REQUEST:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_sc_create_sdo_request(master, arg, priv);
         case EC_IOCTL_SC_VOE:
             if (!(filp->f_mode & FMODE_WRITE))
 				return -EPERM;
@@ -2782,6 +3038,22 @@
 			return ec_cdev_ioctl_domain_queue(master, arg, priv);
         case EC_IOCTL_DOMAIN_STATE:
 			return ec_cdev_ioctl_domain_state(master, arg, priv);
+        case EC_IOCTL_SDO_REQUEST_TIMEOUT:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_sdo_request_timeout(master, arg, priv);
+        case EC_IOCTL_SDO_REQUEST_STATE:
+			return ec_cdev_ioctl_sdo_request_state(master, arg, priv);
+        case EC_IOCTL_SDO_REQUEST_READ:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_sdo_request_read(master, arg, priv);
+        case EC_IOCTL_SDO_REQUEST_WRITE:
+            if (!(filp->f_mode & FMODE_WRITE))
+				return -EPERM;
+			return ec_cdev_ioctl_sdo_request_write(master, arg, priv);
+        case EC_IOCTL_SDO_REQUEST_DATA:
+			return ec_cdev_ioctl_sdo_request_data(master, arg, priv);
         case EC_IOCTL_VOE_SEND_HEADER:
             if (!(filp->f_mode & FMODE_WRITE))
 				return -EPERM;
--- a/master/ioctl.h	Mon Feb 02 13:34:01 2009 +0000
+++ b/master/ioctl.h	Mon Feb 02 13:37:00 2009 +0000
@@ -90,19 +90,25 @@
 #define EC_IOCTL_SC_CLEAR_ENTRIES      EC_IOW(0x23, ec_ioctl_config_pdo_t)
 #define EC_IOCTL_SC_REG_PDO_ENTRY     EC_IOWR(0x24, ec_ioctl_reg_pdo_entry_t)
 #define EC_IOCTL_SC_SDO                EC_IOW(0x25, ec_ioctl_sc_sdo_t)
-#define EC_IOCTL_SC_VOE               EC_IOWR(0x26, ec_ioctl_voe_t)
-#define EC_IOCTL_SC_STATE             EC_IOWR(0x27, ec_ioctl_sc_state_t)
-#define EC_IOCTL_DOMAIN_OFFSET          EC_IO(0x28)
-#define EC_IOCTL_DOMAIN_PROCESS         EC_IO(0x29)
-#define EC_IOCTL_DOMAIN_QUEUE           EC_IO(0x2a)
-#define EC_IOCTL_DOMAIN_STATE         EC_IOWR(0x2b, ec_ioctl_domain_state_t)
-#define EC_IOCTL_VOE_SEND_HEADER       EC_IOW(0x2c, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_REC_HEADER       EC_IOWR(0x2d, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ              EC_IOW(0x2e, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ_NOSYNC       EC_IOW(0x2f, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_WRITE            EC_IOWR(0x30, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_EXEC             EC_IOWR(0x31, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_DATA             EC_IOWR(0x32, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_SDO_REQUEST       EC_IOWR(0x26, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SC_VOE               EC_IOWR(0x27, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_STATE             EC_IOWR(0x28, ec_ioctl_sc_state_t)
+#define EC_IOCTL_DOMAIN_OFFSET          EC_IO(0x29)
+#define EC_IOCTL_DOMAIN_PROCESS         EC_IO(0x2a)
+#define EC_IOCTL_DOMAIN_QUEUE           EC_IO(0x2b)
+#define EC_IOCTL_DOMAIN_STATE         EC_IOWR(0x2c, ec_ioctl_domain_state_t)
+#define EC_IOCTL_SDO_REQUEST_TIMEOUT  EC_IOWR(0x2d, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_STATE    EC_IOWR(0x2e, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_READ     EC_IOWR(0x2f, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_WRITE    EC_IOWR(0x30, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_DATA     EC_IOWR(0x31, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_VOE_SEND_HEADER       EC_IOW(0x32, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_REC_HEADER       EC_IOWR(0x33, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ              EC_IOW(0x34, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ_NOSYNC       EC_IOW(0x35, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_WRITE            EC_IOWR(0x36, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_EXEC             EC_IOWR(0x37, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_DATA             EC_IOWR(0x38, ec_ioctl_voe_t)
 
 /*****************************************************************************/
 
@@ -468,6 +474,22 @@
     uint32_t config_index;
 
     // inputs/outputs
+    uint32_t request_index;
+    uint16_t sdo_index;
+    uint8_t sdo_subindex;
+    size_t size;
+    uint8_t *data;
+    uint32_t timeout;
+    ec_request_state_t state;
+} ec_ioctl_sdo_request_t;
+
+/*****************************************************************************/
+
+typedef struct {
+    // inputs
+    uint32_t config_index;
+
+    // inputs/outputs
     uint32_t voe_index;
     uint32_t *vendor_id;
     uint16_t *vendor_type;