diff --git a/lib/slave_config.c b/lib/slave_config.c
index ea28f75a..4e00fcf9 100644
--- a/lib/slave_config.c
+++ b/lib/slave_config.c
@@ -35,6 +35,7 @@
#include "slave_config.h"
#include "domain.h"
#include "sdo_request.h"
+#include "soe_request.h"
#include "reg_request.h"
#include "voe_handler.h"
#include "master.h"
@@ -44,6 +45,7 @@
void ec_slave_config_clear(ec_slave_config_t *sc)
{
ec_sdo_request_t *r, *next_r;
+ ec_soe_request_t *s, *next_s;
ec_reg_request_t *e, *next_e;
ec_voe_handler_t *v, *next_v;
@@ -56,6 +58,15 @@ void ec_slave_config_clear(ec_slave_config_t *sc)
}
sc->first_sdo_request = NULL;
+ s = sc->first_soe_request;
+ while (r) {
+ next_s = s->next;
+ ec_soe_request_clear(s);
+ free(s);
+ s = next_s;
+ }
+ sc->first_soe_request = NULL;
+
e = sc->first_reg_request;
while (e) {
next_e = e->next;
@@ -607,6 +618,76 @@ ec_sdo_request_t *ecrt_slave_config_create_sdo_request(ec_slave_config_t *sc,
/*****************************************************************************/
+void ec_slave_config_add_soe_request(ec_slave_config_t *sc,
+ ec_soe_request_t *req)
+{
+ if (sc->first_soe_request) {
+ ec_soe_request_t *r = sc->first_soe_request;
+ while (r->next) {
+ r = r->next;
+ }
+ r->next = req;
+ } else {
+ sc->first_soe_request = req;
+ }
+}
+
+/*****************************************************************************/
+
+ec_soe_request_t *ecrt_slave_config_create_soe_request(ec_slave_config_t *sc,
+ uint8_t drive_no, uint16_t idn, size_t size)
+{
+ ec_ioctl_soe_request_t data;
+ ec_soe_request_t *req;
+ int ret;
+
+ req = malloc(sizeof(ec_soe_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 %zu bytes of SoE data"
+ " memory.\n", size);
+ free(req);
+ return 0;
+ }
+ } else {
+ req->data = NULL;
+ }
+
+ data.config_index = sc->index;
+ data.drive_no = drive_no;
+ data.idn = idn;
+ data.size = size;
+
+ ret = ioctl(sc->master->fd, EC_IOCTL_SC_SOE_REQUEST, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to create SoE request: %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ ec_soe_request_clear(req);
+ free(req);
+ return NULL;
+ }
+
+ req->next = NULL;
+ req->config = sc;
+ req->index = data.request_index;
+ req->drive_no = data.drive_no;
+ req->idn = data.idn;
+ req->data_size = size;
+ req->mem_size = size;
+
+ ec_slave_config_add_soe_request(sc, req);
+
+ return req;
+}
+
+/*****************************************************************************/
+
void ec_slave_config_add_reg_request(ec_slave_config_t *sc,
ec_reg_request_t *reg)
{
diff --git a/lib/slave_config.h b/lib/slave_config.h
index 7d855a5f..6e3a4355 100644
--- a/lib/slave_config.h
+++ b/lib/slave_config.h
@@ -1,8 +1,6 @@
/******************************************************************************
*
- * $Id$
- *
- * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
+ * Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT master userspace library.
*
@@ -39,6 +37,7 @@ struct ec_slave_config {
uint16_t alias;
uint16_t position;
ec_sdo_request_t *first_sdo_request;
+ ec_soe_request_t *first_soe_request;
ec_reg_request_t *first_reg_request;
ec_voe_handler_t *first_voe_handler;
};
diff --git a/lib/soe_request.c b/lib/soe_request.c
new file mode 100644
index 00000000..99bc311e
--- /dev/null
+++ b/lib/soe_request.c
@@ -0,0 +1,182 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2006-2023 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
+ * .
+ *
+ * ---
+ *
+ * The license mentioned above concerns the source code only. Using the
+ * EtherCAT technology and brand is only permitted in compliance with the
+ * industrial property and similar rights of Beckhoff Automation GmbH.
+ *
+ *****************************************************************************/
+
+/** \file
+ * Canopen over EtherCAT SoE request functions.
+ */
+
+/*****************************************************************************/
+
+#include
+#include
+
+#include "ioctl.h"
+#include "soe_request.h"
+#include "slave_config.h"
+#include "master.h"
+
+/*****************************************************************************/
+
+void ec_soe_request_clear(ec_soe_request_t *req)
+{
+ if (req->data) {
+ free(req->data);
+ req->data = NULL;
+ }
+}
+
+/*****************************************************************************
+ * Application interface.
+ ****************************************************************************/
+
+void ecrt_soe_request_idn(ec_soe_request_t *req, uint8_t drive_no,
+ uint16_t idn)
+{
+ ec_ioctl_soe_request_t data;
+ int ret;
+
+ data.config_index = req->config->index;
+ data.request_index = req->index;
+ data.drive_no = drive_no;
+ data.idn = idn;
+
+ ret = ioctl(req->config->master->fd, EC_IOCTL_SOE_REQUEST_IDN, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to set SoE request IDN: %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_soe_request_timeout(ec_soe_request_t *req, uint32_t timeout)
+{
+ ec_ioctl_soe_request_t data;
+ int ret;
+
+ data.config_index = req->config->index;
+ data.request_index = req->index;
+ data.timeout = timeout;
+
+ ret = ioctl(req->config->master->fd, EC_IOCTL_SOE_REQUEST_TIMEOUT, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to set SoE request timeout: %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ }
+}
+
+/*****************************************************************************/
+
+uint8_t *ecrt_soe_request_data(ec_soe_request_t *req)
+{
+ return req->data;
+}
+
+/*****************************************************************************/
+
+size_t ecrt_soe_request_data_size(const ec_soe_request_t *req)
+{
+ return req->data_size;
+}
+
+/*****************************************************************************/
+
+ec_request_state_t ecrt_soe_request_state(ec_soe_request_t *req)
+{
+ ec_ioctl_soe_request_t data;
+ int ret;
+
+ data.config_index = req->config->index;
+ data.request_index = req->index;
+
+ ret = ioctl(req->config->master->fd, EC_IOCTL_SOE_REQUEST_STATE, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to get SoE request state: %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ return EC_REQUEST_ERROR;
+ }
+
+ if (data.size) { // new data waiting to be copied
+ if (req->mem_size < data.size) {
+ fprintf(stderr, "Received %zu bytes do not fit info SoE data"
+ " memory (%zu bytes)!\n", data.size, req->mem_size);
+ return EC_REQUEST_ERROR;
+ }
+
+ data.data = req->data;
+
+ ret = ioctl(req->config->master->fd,
+ EC_IOCTL_SOE_REQUEST_DATA, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to get SoE data: %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ return EC_REQUEST_ERROR;
+ }
+ req->data_size = data.size;
+ }
+
+ return data.state;
+}
+
+/*****************************************************************************/
+
+void ecrt_soe_request_read(ec_soe_request_t *req)
+{
+ ec_ioctl_soe_request_t data;
+ int ret;
+
+ data.config_index = req->config->index;
+ data.request_index = req->index;
+
+ ret = ioctl(req->config->master->fd, EC_IOCTL_SOE_REQUEST_READ, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to command an SoE read operation : %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ }
+}
+
+/*****************************************************************************/
+
+void ecrt_soe_request_write(ec_soe_request_t *req)
+{
+ ec_ioctl_soe_request_t data;
+ int ret;
+
+ data.config_index = req->config->index;
+ data.request_index = req->index;
+ data.data = req->data;
+ data.size = req->data_size;
+
+ ret = ioctl(req->config->master->fd, EC_IOCTL_SOE_REQUEST_WRITE, &data);
+ if (EC_IOCTL_IS_ERROR(ret)) {
+ fprintf(stderr, "Failed to command an SDO write operation : %s\n",
+ strerror(EC_IOCTL_ERRNO(ret)));
+ }
+}
+
+/*****************************************************************************/
diff --git a/lib/soe_request.h b/lib/soe_request.h
new file mode 100644
index 00000000..43f8e72d
--- /dev/null
+++ b/lib/soe_request.h
@@ -0,0 +1,48 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2006-2023 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
+ * .
+ *
+ * ---
+ *
+ * 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_soe_request {
+ ec_soe_request_t *next; /**< List header. */
+ ec_slave_config_t *config; /**< Parent slave configuration. */
+ unsigned int index; /**< Request index (identifier). */
+ uint8_t drive_no; /**< SoE drive number. */
+ uint16_t idn; /**< SoE ID number. */
+ uint8_t *data; /**< Pointer to SoE data. */
+ size_t mem_size; /**< Size of SoE data memory. */
+ size_t data_size; /**< Size of SoE data. */
+};
+
+/*****************************************************************************/
+
+void ec_soe_request_clear(ec_soe_request_t *);
+
+/*****************************************************************************/
diff --git a/master/ioctl.c b/master/ioctl.c
index 43a98c1a..18c0cbdc 100644
--- a/master/ioctl.c
+++ b/master/ioctl.c
@@ -1,8 +1,6 @@
/******************************************************************************
*
- * $Id$
- *
- * Copyright (C) 2006-2012 Florian Pose, Ingenieurgemeinschaft IgH
+ * Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH
*
* This file is part of the IgH EtherCAT Master.
*
@@ -2819,6 +2817,59 @@ static ATTRIBUTES int ec_ioctl_sc_create_sdo_request(
/*****************************************************************************/
+/** Create an SoE request.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_sc_create_soe_request(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ data.request_index = 0;
+
+ if (down_interruptible(&master->master_sem))
+ return -EINTR;
+
+ sc = ec_master_get_config(master, data.config_index);
+ if (!sc) {
+ up(&master->master_sem);
+ return -ENOENT;
+ }
+
+ list_for_each_entry(req, &sc->soe_requests, list) {
+ data.request_index++;
+ }
+
+ up(&master->master_sem); /** \todo sc could be invalidated */
+
+ req = ecrt_slave_config_create_soe_request_err(sc, data.drive_no,
+ data.idn, data.size);
+ if (IS_ERR(req)) {
+ return PTR_ERR(req);
+ }
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data))) {
+ return -EFAULT;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************/
+
/** Create a register request.
*
* \return Zero on success, otherwise a negative error code.
@@ -3482,6 +3533,255 @@ static ATTRIBUTES int ec_ioctl_sdo_request_data(
/*****************************************************************************/
+/** Sets an SoE request's drive number and IDN.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_request_index(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_soe_request_idn(req, data.drive_no, data.idn);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Sets an CoE request's timeout.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_request_timeout(
+ ec_master_t *master, /**< EtherCAT master. */
+ void *arg, /**< ioctl() argument. */
+ ec_ioctl_context_t *ctx /**< Private data structure of file handle. */
+ )
+{
+ ec_ioctl_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_soe_request_timeout(req, data.timeout);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Gets an SoE request's state.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_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_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ data.state = ecrt_soe_request_state(req);
+ if (data.state == EC_REQUEST_SUCCESS && req->dir == EC_DIR_INPUT) {
+ data.size = ecrt_soe_request_data_size(req);
+ }
+ else {
+ data.size = 0;
+ }
+
+ if (copy_to_user((void __user *) arg, &data, sizeof(data)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SoE IDN read operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_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_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ecrt_soe_request_read(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Starts an SoE IDN write operation.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_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_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+ int ret;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ if (!data.size) {
+ EC_MASTER_ERR(master, "IDN write: Data size may not be zero!\n");
+ return -EINVAL;
+ }
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ ret = ec_soe_request_alloc(req, data.size);
+ if (ret)
+ return ret;
+
+ if (copy_from_user(req->data, (void __user *) data.data, data.size))
+ return -EFAULT;
+
+ req->data_size = data.size;
+ ecrt_soe_request_write(req);
+ return 0;
+}
+
+/*****************************************************************************/
+
+/** Read SoE IDN data.
+ *
+ * \return Zero on success, otherwise a negative error code.
+ */
+static ATTRIBUTES int ec_ioctl_soe_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_soe_request_t data;
+ ec_slave_config_t *sc;
+ ec_soe_request_t *req;
+
+ if (unlikely(!ctx->requested))
+ return -EPERM;
+
+ if (copy_from_user(&data, (void __user *) arg, sizeof(data)))
+ return -EFAULT;
+
+ /* no locking of master_sem needed, because neither sc nor req will not be
+ * deleted in the meantime. */
+
+ if (!(sc = ec_master_get_config(master, data.config_index))) {
+ return -ENOENT;
+ }
+
+ if (!(req = ec_slave_config_find_soe_request(sc, data.request_index))) {
+ return -ENOENT;
+ }
+
+ if (copy_to_user((void __user *) data.data, ecrt_soe_request_data(req),
+ ecrt_soe_request_data_size(req)))
+ return -EFAULT;
+
+ return 0;
+}
+
+/*****************************************************************************/
+
/** Read register data.
*
* \return Zero on success, otherwise a negative error code.
@@ -4616,6 +4916,13 @@ long EC_IOCTL(
}
ret = ec_ioctl_sc_create_sdo_request(master, arg, ctx);
break;
+ case EC_IOCTL_SC_SOE_REQUEST:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_sc_create_soe_request(master, arg, ctx);
+ break;
case EC_IOCTL_SC_REG_REQUEST:
if (!ctx->writable) {
ret = -EPERM;
@@ -4704,6 +5011,40 @@ long EC_IOCTL(
case EC_IOCTL_SDO_REQUEST_DATA:
ret = ec_ioctl_sdo_request_data(master, arg, ctx);
break;
+ case EC_IOCTL_SOE_REQUEST_IDN:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_soe_request_index(master, arg, ctx);
+ break;
+ case EC_IOCTL_SOE_REQUEST_TIMEOUT:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_soe_request_timeout(master, arg, ctx);
+ break;
+ case EC_IOCTL_SOE_REQUEST_STATE:
+ ret = ec_ioctl_soe_request_state(master, arg, ctx);
+ break;
+ case EC_IOCTL_SOE_REQUEST_READ:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_soe_request_read(master, arg, ctx);
+ break;
+ case EC_IOCTL_SOE_REQUEST_WRITE:
+ if (!ctx->writable) {
+ ret = -EPERM;
+ break;
+ }
+ ret = ec_ioctl_soe_request_write(master, arg, ctx);
+ break;
+ case EC_IOCTL_SOE_REQUEST_DATA:
+ ret = ec_ioctl_soe_request_data(master, arg, ctx);
+ break;
case EC_IOCTL_REG_REQUEST_DATA:
ret = ec_ioctl_reg_request_data(master, arg, ctx);
break;
diff --git a/master/ioctl.h b/master/ioctl.h
index 3991e406..0bdae660 100644
--- a/master/ioctl.h
+++ b/master/ioctl.h
@@ -47,7 +47,7 @@
*
* Increment this when changing the ioctl interface!
*/
-#define EC_IOCTL_VERSION_MAGIC 31
+#define EC_IOCTL_VERSION_MAGIC 32
// Command-line tool
#define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)
@@ -118,34 +118,41 @@
#define EC_IOCTL_SC_EMERG_CLEAR EC_IOW(0x3d, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_EMERG_OVERRUNS EC_IOWR(0x3e, ec_ioctl_sc_emerg_t)
#define EC_IOCTL_SC_SDO_REQUEST EC_IOWR(0x3f, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x40, ec_ioctl_reg_request_t)
-#define EC_IOCTL_SC_VOE EC_IOWR(0x41, ec_ioctl_voe_t)
-#define EC_IOCTL_SC_STATE EC_IOWR(0x42, ec_ioctl_sc_state_t)
-#define EC_IOCTL_SC_IDN EC_IOW(0x43, ec_ioctl_sc_idn_t)
-#define EC_IOCTL_SC_FLAG EC_IOW(0x44, ec_ioctl_sc_flag_t)
-#define EC_IOCTL_DOMAIN_SIZE EC_IO(0x45)
-#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x46)
-#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x47)
-#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x48)
-#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x49, ec_ioctl_domain_state_t)
-#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x4a, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x4b, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x4c, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x4d, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x4e, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x4f, ec_ioctl_sdo_request_t)
-#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x50, ec_ioctl_reg_request_t)
-#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x51, ec_ioctl_reg_request_t)
-#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x52, ec_ioctl_reg_request_t)
-#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x53, ec_ioctl_reg_request_t)
-#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x54, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x55, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ EC_IOW(0x56, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x57, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_WRITE EC_IOWR(0x58, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_EXEC EC_IOWR(0x59, ec_ioctl_voe_t)
-#define EC_IOCTL_VOE_DATA EC_IOWR(0x5a, ec_ioctl_voe_t)
-#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x5b, size_t)
+#define EC_IOCTL_SC_SOE_REQUEST EC_IOWR(0x40, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SC_REG_REQUEST EC_IOWR(0x41, ec_ioctl_reg_request_t)
+#define EC_IOCTL_SC_VOE EC_IOWR(0x42, ec_ioctl_voe_t)
+#define EC_IOCTL_SC_STATE EC_IOWR(0x43, ec_ioctl_sc_state_t)
+#define EC_IOCTL_SC_IDN EC_IOW(0x44, ec_ioctl_sc_idn_t)
+#define EC_IOCTL_SC_FLAG EC_IOW(0x45, ec_ioctl_sc_flag_t)
+#define EC_IOCTL_DOMAIN_SIZE EC_IO(0x46)
+#define EC_IOCTL_DOMAIN_OFFSET EC_IO(0x47)
+#define EC_IOCTL_DOMAIN_PROCESS EC_IO(0x48)
+#define EC_IOCTL_DOMAIN_QUEUE EC_IO(0x49)
+#define EC_IOCTL_DOMAIN_STATE EC_IOWR(0x4a, ec_ioctl_domain_state_t)
+#define EC_IOCTL_SDO_REQUEST_INDEX EC_IOWR(0x4b, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_TIMEOUT EC_IOWR(0x4c, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_STATE EC_IOWR(0x4d, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_READ EC_IOWR(0x4e, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_WRITE EC_IOWR(0x4f, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SDO_REQUEST_DATA EC_IOWR(0x50, ec_ioctl_sdo_request_t)
+#define EC_IOCTL_SOE_REQUEST_IDN EC_IOWR(0x51, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SOE_REQUEST_TIMEOUT EC_IOWR(0x52, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SOE_REQUEST_STATE EC_IOWR(0x53, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SOE_REQUEST_READ EC_IOWR(0x54, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SOE_REQUEST_WRITE EC_IOWR(0x55, ec_ioctl_soe_request_t)
+#define EC_IOCTL_SOE_REQUEST_DATA EC_IOWR(0x56, ec_ioctl_soe_request_t)
+#define EC_IOCTL_REG_REQUEST_DATA EC_IOWR(0x57, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_STATE EC_IOWR(0x58, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_WRITE EC_IOWR(0x59, ec_ioctl_reg_request_t)
+#define EC_IOCTL_REG_REQUEST_READ EC_IOWR(0x5a, ec_ioctl_reg_request_t)
+#define EC_IOCTL_VOE_SEND_HEADER EC_IOW(0x5b, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_REC_HEADER EC_IOWR(0x5c, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ EC_IOW(0x5d, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_READ_NOSYNC EC_IOW(0x5e, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_WRITE EC_IOWR(0x5f, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_EXEC EC_IOWR(0x60, ec_ioctl_voe_t)
+#define EC_IOCTL_VOE_DATA EC_IOWR(0x61, ec_ioctl_voe_t)
+#define EC_IOCTL_SET_SEND_INTERVAL EC_IOW(0x62, size_t)
/*****************************************************************************/
@@ -730,6 +737,22 @@ typedef struct {
/*****************************************************************************/
+typedef struct {
+ // inputs
+ uint32_t config_index;
+
+ // inputs/outputs
+ uint32_t request_index;
+ uint8_t drive_no;
+ uint16_t idn;
+ size_t size;
+ uint8_t *data;
+ uint32_t timeout;
+ ec_request_state_t state;
+} ec_ioctl_soe_request_t;
+
+/*****************************************************************************/
+
typedef struct {
// inputs
uint32_t config_index;
diff --git a/master/slave_config.c b/master/slave_config.c
index 28dc7d35..2617ef49 100644
--- a/master/slave_config.c
+++ b/master/slave_config.c
@@ -535,6 +535,28 @@ ec_sdo_request_t *ec_slave_config_find_sdo_request(
/*****************************************************************************/
+/** Finds a SoE request via its position in the list.
+ *
+ * \return Search result, or NULL.
+ */
+ec_soe_request_t *ec_slave_config_find_soe_request(
+ ec_slave_config_t *sc, /**< Slave configuration. */
+ unsigned int pos /**< Position in the list. */
+ )
+{
+ ec_soe_request_t *req;
+
+ list_for_each_entry(req, &sc->soe_requests, list) {
+ if (pos--)
+ continue;
+ return req;
+ }
+
+ return NULL;
+}
+
+/*****************************************************************************/
+
/** Finds a register handler via its position in the list.
*
* \return Search result, or NULL.
diff --git a/master/slave_config.h b/master/slave_config.h
index 2d518c58..59cbecde 100644
--- a/master/slave_config.h
+++ b/master/slave_config.h
@@ -172,6 +172,8 @@ const ec_flag_t *ec_slave_config_get_flag_by_pos_const(
const ec_slave_config_t *, unsigned int);
ec_sdo_request_t *ec_slave_config_find_sdo_request(ec_slave_config_t *,
unsigned int);
+ec_soe_request_t *ec_slave_config_find_soe_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 *,