From c1fb080b55888971ddb0aa0abcf501fb34e54138 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 14 Mar 2023 16:05:15 +0100 Subject: [PATCH 1/6] Removed Id keywords. --- master/fsm_slave.c | 4 +--- master/sdo_request.h | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/master/fsm_slave.c b/master/fsm_slave.c index 0ddb76f7..3113ba55 100644 --- a/master/fsm_slave.c +++ b/master/fsm_slave.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. * diff --git a/master/sdo_request.h b/master/sdo_request.h index 80b1c101..d1c39135 100644 --- a/master/sdo_request.h +++ b/master/sdo_request.h @@ -1,8 +1,6 @@ /****************************************************************************** * - * $Id$ - * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * From eac4e340eb148cbb535a29399516201dc77558c3 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 14 Mar 2023 16:07:29 +0100 Subject: [PATCH 2/6] Init missing fields. --- master/sdo_request.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/master/sdo_request.c b/master/sdo_request.c index a1ba8997..dc1ff8a0 100644 --- a/master/sdo_request.c +++ b/master/sdo_request.c @@ -1,8 +1,6 @@ /****************************************************************************** * - * $Id$ - * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -61,10 +59,12 @@ void ec_sdo_request_init( req->data = NULL; req->mem_size = 0; req->data_size = 0; - req->dir = EC_DIR_INVALID; req->issue_timeout = 0; // no timeout req->response_timeout = EC_SDO_REQUEST_RESPONSE_TIMEOUT; + req->dir = EC_DIR_INVALID; req->state = EC_INT_REQUEST_INIT; + req->jiffies_start = 0U; + req->jiffies_sent = 0U; req->errno = 0; req->abort_code = 0x00000000; } From 56c455617392ca7c8b24e65addafdce9d2421cab Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 14 Mar 2023 16:12:45 +0100 Subject: [PATCH 3/6] New SoE request API; implemented API in kernel. --- include/ecrt.h | 142 ++++++++++++++++++++++++++++++++++++++++-- master/fsm_master.c | 117 ++++++++++++++++++++++++++++------ master/fsm_master.h | 5 +- master/slave_config.c | 67 ++++++++++++++++++-- master/slave_config.h | 7 ++- master/soe_request.c | 87 +++++++++++++++++++++++++- master/soe_request.h | 8 ++- 7 files changed, 395 insertions(+), 38 deletions(-) diff --git a/include/ecrt.h b/include/ecrt.h index 3f6b4b19..40bcec25 100644 --- a/include/ecrt.h +++ b/include/ecrt.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. * @@ -43,6 +41,13 @@ * * - Added the ecrt_slave_config_flag() method and the EC_HAVE_FLAGS * definition to check for its existence. + * - Added SoE IDN requests, including the datatype ec_soe_request_t and the + * methods ecrt_slave_config_create_soe_request(), + * ecrt_soe_request_object(), ecrt_soe_request_timeout(), + * ecrt_soe_request_data(), ecrt_soe_request_data_size(), + * ecrt_soe_request_state(), ecrt_soe_request_write() and + * ecrt_soe_request_read(). Use the EC_HAVE_SOE_REQUESTS to check, if the + * functionality is available. * * Changes in version 1.5.2: * @@ -52,7 +57,7 @@ * - Added the EC_HAVE_REDUNDANCY define, to check, if the interface contains * redundancy features. * - Added ecrt_sdo_request_index() to change SDO index and subindex after - * handler creation. + * request creation. * - Added interface for retrieving CoE emergency messages, i. e. * ecrt_slave_config_emerg_size(), ecrt_slave_config_emerg_pop(), * ecrt_slave_config_emerg_clear(), ecrt_slave_config_emerg_overruns() and @@ -200,6 +205,14 @@ */ #define EC_HAVE_FLAGS +/** Defined if the methods ecrt_slave_config_create_soe_request(), + * ecrt_soe_request_object(), ecrt_soe_request_timeout(), + * ecrt_soe_request_data(), ecrt_soe_request_data_size(), + * ecrt_soe_request_state(), ecrt_soe_request_write() and + * ecrt_soe_request_read() and the datatype ec_soe_request_t are available. + */ +#define EC_HAVE_SOE_REQUESTS + /*****************************************************************************/ /** End of list marker. @@ -254,11 +267,14 @@ typedef struct ec_domain ec_domain_t; /**< \see ec_domain */ struct ec_sdo_request; typedef struct ec_sdo_request ec_sdo_request_t; /**< \see ec_sdo_request. */ +struct ec_soe_request; +typedef struct ec_soe_request ec_soe_request_t; /**< \see ec_soe_request. */ + struct ec_voe_handler; typedef struct ec_voe_handler ec_voe_handler_t; /**< \see ec_voe_handler. */ struct ec_reg_request; -typedef struct ec_reg_request ec_reg_request_t; /**< \see ec_sdo_request. */ +typedef struct ec_reg_request ec_reg_request_t; /**< \see ec_reg_request. */ /*****************************************************************************/ @@ -1559,6 +1575,23 @@ ec_sdo_request_t *ecrt_slave_config_create_sdo_request( size_t size /**< Data size to reserve. */ ); +/** Create an SoE request to exchange SoE IDNs during realtime operation. + * + * The created SoE request object is freed automatically when the master is + * released. + * + * This method has to be called in non-realtime context before + * ecrt_master_activate(). + * + * \return New SoE request, or NULL on error. + */ +ec_soe_request_t *ecrt_slave_config_create_soe_request( + ec_slave_config_t *sc, /**< Slave configuration. */ + uint8_t drive_no, /**< Drive number. */ + uint16_t idn, /**< Sercos ID-Number. */ + size_t size /**< Data size to reserve. */ + ); + /** Create an VoE handler to exchange vendor-specific data during realtime * operation. * @@ -1874,6 +1907,105 @@ void ecrt_sdo_request_read( ec_sdo_request_t *req /**< SDO request. */ ); +/***************************************************************************** + * SoE request methods. + ****************************************************************************/ + +/** Set the request's drive and Sercos ID numbers. + * + * \attention If the drive number and/or IDN is changed while + * ecrt_soe_request_state() returns EC_REQUEST_BUSY, this may lead to + * unexpected results. + */ +void ecrt_soe_request_idn( + ec_soe_request_t *req, /**< IDN request. */ + uint8_t drive_no, /**< SDO index. */ + uint16_t idn /**< SoE IDN. */ + ); + +/** Set the timeout for an SoE request. + * + * If the request cannot be processed in the specified time, if will be marked + * as failed. + * + * The timeout is permanently stored in the request object and is valid until + * the next call of this method. + */ +void ecrt_soe_request_timeout( + ec_soe_request_t *req, /**< SoE request. */ + uint32_t timeout /**< Timeout in milliseconds. Zero means no + timeout. */ + ); + +/** Access to the SoE request's data. + * + * This function returns a pointer to the request's internal IDN data memory. + * + * - After a read operation was successful, integer data can be evaluated + * using the EC_READ_*() macros as usual. Example: + * \code + * uint16_t value = EC_READ_U16(ecrt_soe_request_data(idn_req))); + * \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_soe_request(). + * \code + * EC_WRITE_U16(ecrt_soe_request_data(idn_req), 0xFFFF); + * \endcode + * + * \attention The return value can be invalidated during a read operation, + * because the internal IDN data memory could be re-allocated if the read IDN + * data do not fit inside. + * + * \return Pointer to the internal IDN data memory. + */ +uint8_t *ecrt_soe_request_data( + ec_soe_request_t *req /**< SoE request. */ + ); + +/** Returns the current IDN data size. + * + * When the SoE request is created, the data size is set to the size of the + * reserved memory. After a read operation the size is set to the size of the + * read data. The size is not modified in any other situation. + * + * \return IDN data size in bytes. + */ +size_t ecrt_soe_request_data_size( + const ec_soe_request_t *req /**< SoE request. */ + ); + +/** Get the current state of the SoE request. + * + * \return Request state. + */ +ec_request_state_t ecrt_soe_request_state( + ec_soe_request_t *req /**< SoE request. */ + ); + +/** Schedule an SoE IDN write operation. + * + * \attention This method may not be called while ecrt_soe_request_state() + * returns EC_REQUEST_BUSY. + */ +void ecrt_soe_request_write( + ec_soe_request_t *req /**< SoE request. */ + ); + +/** Schedule an SoE IDN read operation. + * + * \attention This method may not be called while ecrt_soe_request_state() + * returns EC_REQUEST_BUSY. + * + * \attention After calling this function, the return value of + * ecrt_soe_request_data() must be considered as invalid while + * ecrt_soe_request_state() returns EC_REQUEST_BUSY. + */ +void ecrt_soe_request_read( + ec_soe_request_t *req /**< SoE request. */ + ); + /***************************************************************************** * VoE handler methods. ****************************************************************************/ diff --git a/master/fsm_master.c b/master/fsm_master.c index d2532510..92595dda 100644 --- a/master/fsm_master.c +++ b/master/fsm_master.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. * @@ -66,6 +64,7 @@ void ec_fsm_master_state_assign_sii(ec_fsm_master_t *); void ec_fsm_master_state_write_sii(ec_fsm_master_t *); void ec_fsm_master_state_sdo_dictionary(ec_fsm_master_t *); void ec_fsm_master_state_sdo_request(ec_fsm_master_t *); +void ec_fsm_master_state_soe_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 *); @@ -83,8 +82,18 @@ void ec_fsm_master_init( fsm->master = master; fsm->datagram = datagram; + // inits the member variables state, idle, dev_idx, link_state, + // slaves_responding, slave_states and rescan_required ec_fsm_master_reset(fsm); + fsm->retries = 0; + fsm->scan_jiffies = 0; + fsm->slave = NULL; + fsm->sii_request = NULL; + fsm->sii_index = 0; + fsm->sdo_request = NULL; + fsm->soe_request = NULL; + // init sub-state-machines ec_fsm_coe_init(&fsm->fsm_coe); ec_fsm_soe_init(&fsm->fsm_soe); @@ -492,17 +501,18 @@ int ec_fsm_master_action_process_sii( /*****************************************************************************/ -/** Check for pending SDO requests and process one. +/** Check for pending internal SDO/SoE requests and process one. * * \return non-zero, if an SDO request is processed. */ -int ec_fsm_master_action_process_sdo( +int ec_fsm_master_action_process_int_request( ec_fsm_master_t *fsm /**< Master state machine. */ ) { ec_master_t *master = fsm->master; ec_slave_t *slave; - ec_sdo_request_t *req; + ec_sdo_request_t *sdo_req; + ec_soe_request_t *soe_req; // search for internal requests to be processed for (slave = master->slaves; @@ -513,33 +523,61 @@ int ec_fsm_master_action_process_sdo( continue; } - list_for_each_entry(req, &slave->config->sdo_requests, list) { - if (req->state == EC_INT_REQUEST_QUEUED) { + list_for_each_entry(sdo_req, &slave->config->sdo_requests, list) { + if (sdo_req->state == EC_INT_REQUEST_QUEUED) { - if (ec_sdo_request_timed_out(req)) { - req->state = EC_INT_REQUEST_FAILURE; + if (ec_sdo_request_timed_out(sdo_req)) { + sdo_req->state = EC_INT_REQUEST_FAILURE; EC_SLAVE_DBG(slave, 1, "Internal SDO request" " timed out.\n"); continue; } if (slave->current_state == EC_SLAVE_STATE_INIT) { - req->state = EC_INT_REQUEST_FAILURE; + sdo_req->state = EC_INT_REQUEST_FAILURE; continue; } - req->state = EC_INT_REQUEST_BUSY; + sdo_req->state = EC_INT_REQUEST_BUSY; EC_SLAVE_DBG(slave, 1, "Processing internal" " SDO request...\n"); fsm->idle = 0; - fsm->sdo_request = req; + fsm->sdo_request = sdo_req; fsm->slave = slave; fsm->state = ec_fsm_master_state_sdo_request; - ec_fsm_coe_transfer(&fsm->fsm_coe, slave, req); + ec_fsm_coe_transfer(&fsm->fsm_coe, slave, sdo_req); ec_fsm_coe_exec(&fsm->fsm_coe, fsm->datagram); return 1; } } + + list_for_each_entry(soe_req, &slave->config->soe_requests, list) { + if (soe_req->state == EC_INT_REQUEST_QUEUED) { + + if (ec_soe_request_timed_out(soe_req)) { + soe_req->state = EC_INT_REQUEST_FAILURE; + EC_SLAVE_DBG(slave, 1, "Internal SoE request" + " timed out.\n"); + continue; + } + + if (slave->current_state == EC_SLAVE_STATE_INIT) { + soe_req->state = EC_INT_REQUEST_FAILURE; + continue; + } + + soe_req->state = EC_INT_REQUEST_BUSY; + EC_SLAVE_DBG(slave, 1, "Processing internal" + " SoE request...\n"); + fsm->idle = 0; + fsm->soe_request = soe_req; + fsm->slave = slave; + fsm->state = ec_fsm_master_state_soe_request; + ec_fsm_soe_transfer(&fsm->fsm_soe, slave, soe_req); + ec_fsm_soe_exec(&fsm->fsm_soe, fsm->datagram); + return 1; + } + } } return 0; } @@ -557,8 +595,8 @@ void ec_fsm_master_action_idle( ec_master_t *master = fsm->master; ec_slave_t *slave; - // Check for pending internal SDO requests - if (ec_fsm_master_action_process_sdo(fsm)) { + // Check for pending internal SDO or SoE requests + if (ec_fsm_master_action_process_int_request(fsm)) { return; } @@ -1357,8 +1395,51 @@ void ec_fsm_master_state_sdo_request( EC_SLAVE_DBG(fsm->slave, 1, "Finished internal SDO request.\n"); - // check for another SDO request - if (ec_fsm_master_action_process_sdo(fsm)) { + // check for another SDO/SoE request + if (ec_fsm_master_action_process_int_request(fsm)) { + return; // processing another request + } + + ec_fsm_master_restart(fsm); +} + +/*****************************************************************************/ + +/** Master state: SoE REQUEST. + */ +void ec_fsm_master_state_soe_request( + ec_fsm_master_t *fsm /**< Master state machine. */ + ) +{ + ec_soe_request_t *request = fsm->soe_request; + + if (!request) { + // configuration was cleared in the meantime + ec_fsm_master_restart(fsm); + return; + } + + if (ec_fsm_soe_exec(&fsm->fsm_soe, fsm->datagram)) { + return; + } + + if (!ec_fsm_soe_success(&fsm->fsm_soe)) { + EC_SLAVE_DBG(fsm->slave, 1, + "Failed to process internal SoE request.\n"); + request->state = EC_INT_REQUEST_FAILURE; + wake_up_all(&fsm->master->request_queue); + ec_fsm_master_restart(fsm); + return; + } + + // SoE request finished + request->state = EC_INT_REQUEST_SUCCESS; + wake_up_all(&fsm->master->request_queue); + + EC_SLAVE_DBG(fsm->slave, 1, "Finished internal SoE request.\n"); + + // check for another CoE/SoE request + if (ec_fsm_master_action_process_int_request(fsm)) { return; // processing another request } diff --git a/master/fsm_master.h b/master/fsm_master.h index a41b949c..3e461b43 100644 --- a/master/fsm_master.h +++ b/master/fsm_master.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. * @@ -88,6 +86,7 @@ struct ec_fsm_master { 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_soe_request_t *soe_request; /**< SoE request to process. */ ec_fsm_coe_t fsm_coe; /**< CoE state machine */ ec_fsm_soe_t fsm_soe; /**< SoE state machine */ diff --git a/master/slave_config.c b/master/slave_config.c index f2c45a30..28dc7d35 100644 --- a/master/slave_config.c +++ b/master/slave_config.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. * @@ -87,6 +85,7 @@ void ec_slave_config_init( INIT_LIST_HEAD(&sc->sdo_configs); INIT_LIST_HEAD(&sc->sdo_requests); + INIT_LIST_HEAD(&sc->soe_requests); INIT_LIST_HEAD(&sc->reg_requests); INIT_LIST_HEAD(&sc->voe_handlers); INIT_LIST_HEAD(&sc->soe_configs); @@ -132,6 +131,13 @@ void ec_slave_config_clear( kfree(req); } + // free all SoE requests + list_for_each_entry_safe(soe, next_soe, &sc->soe_requests, list) { + list_del(&soe->list); + ec_soe_request_clear(soe); + kfree(soe); + } + // free all register requests list_for_each_entry_safe(reg, next_reg, &sc->reg_requests, list) { list_del(®->list); @@ -507,7 +513,7 @@ const ec_flag_t *ec_slave_config_get_flag_by_pos_const( /*****************************************************************************/ -/** Finds a CoE handler via its position in the list. +/** Finds a CoE SDO request via its position in the list. * * \return Search result, or NULL. */ @@ -1158,6 +1164,58 @@ ec_sdo_request_t *ecrt_slave_config_create_sdo_request( /*****************************************************************************/ +/** Same as ecrt_slave_config_create_soe_request(), but with ERR_PTR() return + * value. + */ +ec_soe_request_t *ecrt_slave_config_create_soe_request_err( + ec_slave_config_t *sc, uint8_t drive_no, uint16_t idn, size_t size) +{ + ec_soe_request_t *req; + int ret; + + EC_CONFIG_DBG(sc, 1, "%s(sc = 0x%p, " + "drive_no = 0x%02X, idn = 0x%04X, size = %zu)\n", + __func__, sc, drive_no, idn, size); + + if (!(req = (ec_soe_request_t *) + kmalloc(sizeof(ec_soe_request_t), GFP_KERNEL))) { + EC_CONFIG_ERR(sc, "Failed to allocate IDN request memory!\n"); + return ERR_PTR(-ENOMEM); + } + + ec_soe_request_init(req); + ecrt_soe_request_idn(req, drive_no, idn); + + ret = ec_soe_request_alloc(req, size); + if (ret < 0) { + ec_soe_request_clear(req); + kfree(req); + return ERR_PTR(ret); + } + + // prepare data for optional writing + memset(req->data, 0x00, size); + req->data_size = size; + + down(&sc->master->master_sem); + list_add_tail(&req->list, &sc->soe_requests); + up(&sc->master->master_sem); + + return 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_soe_request_t *req = ecrt_slave_config_create_soe_request_err(sc, + drive_no, idn, size); + return IS_ERR(req) ? NULL : req; +} + +/*****************************************************************************/ + /** Same as ecrt_slave_config_create_reg_request(), but with ERR_PTR() return * value. */ @@ -1373,6 +1431,7 @@ EXPORT_SYMBOL(ecrt_slave_config_emerg_pop); EXPORT_SYMBOL(ecrt_slave_config_emerg_clear); EXPORT_SYMBOL(ecrt_slave_config_emerg_overruns); EXPORT_SYMBOL(ecrt_slave_config_create_sdo_request); +EXPORT_SYMBOL(ecrt_slave_config_create_soe_request); EXPORT_SYMBOL(ecrt_slave_config_create_voe_handler); EXPORT_SYMBOL(ecrt_slave_config_create_reg_request); EXPORT_SYMBOL(ecrt_slave_config_state); diff --git a/master/slave_config.h b/master/slave_config.h index ad22e4ed..2d518c58 100644 --- a/master/slave_config.h +++ b/master/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. * @@ -143,6 +141,7 @@ struct ec_slave_config { struct list_head sdo_configs; /**< List of SDO configurations. */ struct list_head sdo_requests; /**< List of SDO requests. */ + struct list_head soe_requests; /**< List of SoE 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. */ @@ -181,6 +180,8 @@ ec_flag_t *ec_slave_config_find_flag(ec_slave_config_t *, const char *); ec_sdo_request_t *ecrt_slave_config_create_sdo_request_err( ec_slave_config_t *, uint16_t, uint8_t, size_t); +ec_soe_request_t *ecrt_slave_config_create_soe_request_err( + ec_slave_config_t *, uint8_t, uint16_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( diff --git a/master/soe_request.c b/master/soe_request.c index 4ad90353..1dd28b2c 100644 --- a/master/soe_request.c +++ b/master/soe_request.c @@ -1,8 +1,6 @@ /****************************************************************************** * - * $Id$ - * - * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH + * Copyright (C) 2006-2023 Florian Pose, Ingenieurgemeinschaft IgH * * This file is part of the IgH EtherCAT Master. * @@ -64,8 +62,10 @@ void ec_soe_request_init( req->data = NULL; req->mem_size = 0; req->data_size = 0; + req->issue_timeout = 0; // no timeout req->dir = EC_DIR_INVALID; req->state = EC_INT_REQUEST_INIT; + req->jiffies_start = 0U; req->jiffies_sent = 0U; req->error_code = 0x0000; } @@ -236,6 +236,7 @@ void ec_soe_request_read( req->dir = EC_DIR_INPUT; req->state = EC_INT_REQUEST_QUEUED; req->error_code = 0x0000; + req->jiffies_start = jiffies; } /*****************************************************************************/ @@ -249,6 +250,86 @@ void ec_soe_request_write( req->dir = EC_DIR_OUTPUT; req->state = EC_INT_REQUEST_QUEUED; req->error_code = 0x0000; + req->jiffies_start = jiffies; } /*****************************************************************************/ + +/** Checks, if the timeout was exceeded. + * + * \return non-zero if the timeout was exceeded, else zero. + */ +int ec_soe_request_timed_out(const ec_soe_request_t *req /**< SDO request. */) +{ + return req->issue_timeout + && jiffies - req->jiffies_start > HZ * req->issue_timeout / 1000; +} + +/***************************************************************************** + * Application interface. + ****************************************************************************/ + +void ecrt_soe_request_idn(ec_soe_request_t *req, uint8_t drive_no, + uint16_t idn) +{ + req->drive_no = drive_no; + req->idn = idn; +} + +/*****************************************************************************/ + +void ecrt_soe_request_timeout(ec_soe_request_t *req, uint32_t timeout) +{ + req->issue_timeout = timeout; +} + +/*****************************************************************************/ + +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) +{ + return ec_request_state_translation_table[req->state]; +} + +/*****************************************************************************/ + +void ecrt_soe_request_read(ec_soe_request_t *req) +{ + ec_soe_request_read(req); +} + +/*****************************************************************************/ + +void ecrt_soe_request_write(ec_soe_request_t *req) +{ + ec_soe_request_write(req); +} + +/*****************************************************************************/ + +/** \cond */ + +EXPORT_SYMBOL(ecrt_soe_request_idn); +EXPORT_SYMBOL(ecrt_soe_request_timeout); +EXPORT_SYMBOL(ecrt_soe_request_data); +EXPORT_SYMBOL(ecrt_soe_request_data_size); +EXPORT_SYMBOL(ecrt_soe_request_state); +EXPORT_SYMBOL(ecrt_soe_request_read); +EXPORT_SYMBOL(ecrt_soe_request_write); + +/** \endcond */ + +/*****************************************************************************/ diff --git a/master/soe_request.h b/master/soe_request.h index 40f61aa6..c5ff9404 100644 --- a/master/soe_request.h +++ b/master/soe_request.h @@ -45,7 +45,7 @@ /** Sercos-over-EtherCAT request. */ -typedef struct { +struct ec_soe_request { struct list_head list; /**< List item. */ uint8_t drive_no; /**< Drive number. */ uint16_t idn; /**< Sercos ID-Number. */ @@ -53,13 +53,16 @@ typedef struct { uint8_t *data; /**< Pointer to SDO data. */ size_t mem_size; /**< Size of SDO data memory. */ size_t data_size; /**< Size of SDO data. */ + uint32_t issue_timeout; /**< Maximum time in ms, the processing of the + request may take. */ ec_direction_t dir; /**< Direction. EC_DIR_OUTPUT means writing to the slave, EC_DIR_INPUT means reading from the slave. */ ec_internal_request_state_t state; /**< Request state. */ + unsigned long jiffies_start; /**< Jiffies, when the request was issued. */ unsigned long jiffies_sent; /**< Jiffies, when the upload/download request was sent. */ uint16_t error_code; /**< SoE error code. */ -} ec_soe_request_t; +}; /*****************************************************************************/ @@ -74,6 +77,7 @@ int ec_soe_request_copy_data(ec_soe_request_t *, const uint8_t *, size_t); int ec_soe_request_append_data(ec_soe_request_t *, const uint8_t *, size_t); void ec_soe_request_read(ec_soe_request_t *); void ec_soe_request_write(ec_soe_request_t *); +int ec_soe_request_timed_out(const ec_soe_request_t *); /*****************************************************************************/ From bdab9570f22c7daf4a23810609267cecb18aa82c Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 14 Mar 2023 16:48:48 +0100 Subject: [PATCH 4/6] Implemented SoE requests in user-space library. --- lib/slave_config.c | 81 ++++++++++ lib/slave_config.h | 5 +- lib/soe_request.c | 182 ++++++++++++++++++++++ lib/soe_request.h | 48 ++++++ master/ioctl.c | 347 +++++++++++++++++++++++++++++++++++++++++- master/ioctl.h | 81 ++++++---- master/slave_config.c | 22 +++ master/slave_config.h | 2 + 8 files changed, 733 insertions(+), 35 deletions(-) create mode 100644 lib/soe_request.c create mode 100644 lib/soe_request.h 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 *, From ff79ec3a5945c0d5e0317dfd00de68a7636f7e54 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Tue, 14 Mar 2023 17:11:20 +0100 Subject: [PATCH 5/6] Added SoE requests to library; updated library version. --- lib/Makefile.am | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 330c1e34..ebad06b4 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 @@ libethercat_la_SOURCES = \ reg_request.c \ sdo_request.c \ slave_config.c \ + soe_request.c \ voe_handler.c noinst_HEADERS = \ @@ -48,6 +47,7 @@ noinst_HEADERS = \ reg_request.h \ sdo_request.h \ slave_config.h \ + soe_request.h \ voe_handler.h libethercat_la_CFLAGS = -fno-strict-aliasing -Wall -I$(top_srcdir) @@ -70,8 +70,10 @@ libethercat_la_CFLAGS = -fno-strict-aliasing -Wall -I$(top_srcdir) # 1:0.0 # ecrt_master_sync_reference_clock_to() added. # 2:0:1 +# SoE requests added +# 3:0:2 # -libethercat_la_LDFLAGS = -version-info 2:0:1 +libethercat_la_LDFLAGS = -version-info 3:0:2 pkgconfig_DATA = libethercat.pc From 5c6e19981bdd76697e702eff6a5c7c81c45ecf30 Mon Sep 17 00:00:00 2001 From: Florian Pose Date: Thu, 18 Jan 2024 10:37:27 +0100 Subject: [PATCH 6/6] Incremented ioctl version magic to 33. --- master/ioctl.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/master/ioctl.h b/master/ioctl.h index 0bdae660..4e68f084 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 32 +#define EC_IOCTL_VERSION_MAGIC 33 // Command-line tool #define EC_IOCTL_MODULE EC_IOR(0x00, ec_ioctl_module_t)