Improved the callback mechanism.

This commit is contained in:
Florian Pose 2009-07-13 15:18:28 +00:00
parent 9e937eb4fc
commit a3c32e8b67
10 changed files with 193 additions and 124 deletions

4
NEWS
View File

@ -12,6 +12,10 @@ Changes since 1.4.0:
library is licensed under LGPLv2.
* Added distributed clocks support.
* Added VoE mailbox protocol support.
* Improved the callback mechanism. ecrt_master_callbacks() now takes two
callback functions for sending and receiving datagrams.
ecrt_master_send_ext() is used to execute the sending of non-application
datagrams.
* Separated datagram initialization from filling the payload with zeros.
Introduced new method ec_datagram_zero() for that.
* Added phy_read and phy_write commands to ethercat tool.

3
TODO
View File

@ -24,8 +24,6 @@ Version 1.5.0:
* Remove byte-swapping functions from user space.
* EoE:
- Only execute one EoE handler per cycle.
- Replace locking callbacks.
- Allow EoE with userspace application.
* Implement 'ethercat foe_read --output-file ...'.
* Fix unloading problem of ec_e100 driver.
* Use ec_datagram_zero() where possible.
@ -41,7 +39,6 @@ Version 1.5.0:
* Remove ecrt_domain_state()?
* Check force_config flag before error.
* Remove allow_scanning flag.
* Implement ecrt_master_slave() in kernel space.
* Check for ioctl() interface version.
* Improve application-triggered SDO transfers by moving the state machine into
the SDO handlers.

View File

@ -58,7 +58,6 @@
// EtherCAT
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
@ -125,9 +124,9 @@ void check_domain1_state(void)
{
ec_domain_state_t ds;
spin_lock(&master_lock);
rt_sem_wait(&master_sem);
ecrt_domain_state(domain1, &ds);
spin_unlock(&master_lock);
rt_sem_signal(&master_sem);
if (ds.working_counter != domain1_state.working_counter)
printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);
@ -143,9 +142,9 @@ void check_master_state(void)
{
ec_master_state_t ms;
spin_lock(&master_lock);
rt_sem_wait(&master_sem);
ecrt_master_state(master, &ms);
spin_unlock(&master_lock);
rt_sem_signal(&master_sem);
if (ms.slaves_responding != master_state.slaves_responding)
printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
@ -239,21 +238,26 @@ void run(long data)
/*****************************************************************************/
int request_lock(void *data)
void send_callback(ec_master_t *master)
{
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle > t_critical) return -1;
// allow access
rt_sem_wait(&master_sem);
return 0;
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_send_ext(master);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
void release_lock(void *data)
void receive_callback(ec_master_t *master)
{
rt_sem_signal(&master_sem);
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
@ -277,7 +281,7 @@ int __init init_mod(void)
goto out_return;
}
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
ecrt_master_callbacks(master, send_callback, receive_callback);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@ -29,7 +29,7 @@
#include <linux/module.h>
#include <linux/timer.h>
#include <linux/spinlock.h>
#include <linux/semaphore.h>
#include <linux/interrupt.h>
#include <linux/err.h>
@ -54,7 +54,7 @@
// EtherCAT
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
struct semaphore master_sem;
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
@ -192,9 +192,9 @@ void check_domain1_state(void)
{
ec_domain_state_t ds;
spin_lock(&master_lock);
down(&master_sem);
ecrt_domain_state(domain1, &ds);
spin_unlock(&master_lock);
up(&master_sem);
if (ds.working_counter != domain1_state.working_counter)
printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);
@ -210,9 +210,9 @@ void check_master_state(void)
{
ec_master_state_t ms;
spin_lock(&master_lock);
down(&master_sem);
ecrt_master_state(master, &ms);
spin_unlock(&master_lock);
up(&master_sem);
if (ms.slaves_responding != master_state.slaves_responding)
printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
@ -230,9 +230,9 @@ void check_slave_config_states(void)
{
ec_slave_config_state_t s;
spin_lock(&master_lock);
down(&master_sem);
ecrt_slave_config_state(sc_ana_in, &s);
spin_unlock(&master_lock);
up(&master_sem);
if (s.al_state != sc_ana_in_state.al_state)
printk(KERN_INFO PFX "AnaIn: State 0x%02X.\n", s.al_state);
@ -300,10 +300,10 @@ void read_voe(void)
void cyclic_task(unsigned long data)
{
// receive process data
spin_lock(&master_lock);
down(&master_sem);
ecrt_master_receive(master);
ecrt_domain_process(domain1);
spin_unlock(&master_lock);
up(&master_sem);
// check process data state (optional)
check_domain1_state();
@ -336,10 +336,10 @@ void cyclic_task(unsigned long data)
EC_WRITE_U8(domain1_pd + off_dig_out, blink ? 0x06 : 0x09);
// send process data
spin_lock(&master_lock);
down(&master_sem);
ecrt_domain_queue(domain1);
ecrt_master_send(master);
spin_unlock(&master_lock);
up(&master_sem);
// restart timer
timer.expires += HZ / FREQUENCY;
@ -348,17 +348,20 @@ void cyclic_task(unsigned long data)
/*****************************************************************************/
int request_lock(void *data)
void send_callback(ec_master_t *master)
{
spin_lock(&master_lock);
return 0; // access allowed
down(&master_sem);
ecrt_master_send_ext(master);
up(&master_sem);
}
/*****************************************************************************/
void release_lock(void *data)
void receive_callback(ec_master_t *master)
{
spin_unlock(&master_lock);
down(&master_sem);
ecrt_master_receive(master);
up(&master_sem);
}
/*****************************************************************************/
@ -382,7 +385,8 @@ int __init init_mini_module(void)
goto out_return;
}
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
init_MUTEX(&master_sem);
ecrt_master_callbacks(master, send_callback, receive_callback);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@ -59,7 +59,6 @@
// EtherCAT
static ec_master_t *master = NULL;
static ec_master_state_t master_state = {};
spinlock_t master_lock = SPIN_LOCK_UNLOCKED;
static ec_domain_t *domain1 = NULL;
static ec_domain_state_t domain1_state = {};
@ -146,9 +145,9 @@ void check_domain1_state(void)
{
ec_domain_state_t ds;
spin_lock(&master_lock);
rt_sem_wait(&master_sem);
ecrt_domain_state(domain1, &ds);
spin_unlock(&master_lock);
rt_sem_signal(&master_sem);
if (ds.working_counter != domain1_state.working_counter)
printk(KERN_INFO PFX "Domain1: WC %u.\n", ds.working_counter);
@ -164,9 +163,9 @@ void check_master_state(void)
{
ec_master_state_t ms;
spin_lock(&master_lock);
rt_sem_wait(&master_sem);
ecrt_master_state(master, &ms);
spin_unlock(&master_lock);
rt_sem_signal(&master_sem);
if (ms.slaves_responding != master_state.slaves_responding)
printk(KERN_INFO PFX "%u slave(s).\n", ms.slaves_responding);
@ -184,9 +183,9 @@ void check_slave_config_states(void)
{
ec_slave_config_state_t s;
spin_lock(&master_lock);
rt_sem_wait(&master_sem);
ecrt_slave_config_state(sc_ana_in, &s);
spin_unlock(&master_lock);
rt_sem_signal(&master_sem);
if (s.al_state != sc_ana_in_state.al_state)
printk(KERN_INFO PFX "AnaIn: State 0x%02X.\n", s.al_state);
@ -244,21 +243,26 @@ void run(long data)
/*****************************************************************************/
int request_lock(void *data)
void send_callback(ec_master_t *master)
{
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle > t_critical) return -1;
// allow access
rt_sem_wait(&master_sem);
return 0;
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_send_ext(master);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
void release_lock(void *data)
void receive_callback(ec_master_t *master)
{
rt_sem_signal(&master_sem);
// too close to the next real time cycle: deny access...
if (get_cycles() - t_last_cycle <= t_critical) {
rt_sem_wait(&master_sem);
ecrt_master_receive(master);
rt_sem_signal(&master_sem);
}
}
/*****************************************************************************/
@ -277,7 +281,6 @@ int __init init_mod(void)
t_critical = cpu_khz * 1000 / FREQUENCY - cpu_khz * INHIBIT_TIME / 1000;
master = ecrt_request_master(0);
if (IS_ERR(master)) {
ret = PTR_ERR(master);
@ -285,7 +288,7 @@ int __init init_mod(void)
goto out_return;
}
ecrt_master_callbacks(master, request_lock, release_lock, NULL);
ecrt_master_callbacks(master, send_callback, receive_callback);
printk(KERN_INFO PFX "Registering domain...\n");
if (!(domain1 = ecrt_master_create_domain(master))) {

View File

@ -42,24 +42,27 @@
* Changes in version 1.5:
*
* - Added the distributed clocks feature and the respective method
* ecrt_slave_config_dc() to configure a slave for cyclic
* operation, and ecrt_master_application_time(),
* ecrt_master_sync_reference_clock() and ecrt_master_sync_slave_clocks()
* for offset and drift compensation. The EC_TIMEVAL2NANO() macro can be
* used for epoch time conversion.
* ecrt_slave_config_dc() to configure a slave for cyclic operation, and
* ecrt_master_application_time(), ecrt_master_sync_reference_clock() and
* ecrt_master_sync_slave_clocks() for offset and drift compensation. The
* EC_TIMEVAL2NANO() macro can be used for epoch time conversion.
* - Improved the callback mechanism. ecrt_master_callbacks() now takes two
* callback functions for sending and receiving datagrams.
* ecrt_master_send_ext() is used to execute the sending of non-application
* datagrams.
* - Added ecrt_open_master() and ecrt_master_reserve() separation for
* userspace.
* - Added ecrt_master() userspace interface, to get information about a
* master.
* - Added ecrt_master_slave() to get information about a certain slave.
* - Added ecrt_slave_sdo_upload() and ecrt_slave_sdo_download() methods to
* let an application transfer SDOs before activating the master.
* - Changed the meaning of the negative return values of
* ecrt_slave_config_reg_pdo_entry() and ecrt_slave_config_sdo*().
* - Imlemented the Vendor-specific over EtherCAT mailbox protocol. See
* ecrt_slave_config_create_voe_handler().
* - 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.
* - Added ecrt_slave_sdo_upload() and ecrt_slave_sdo_download() methods to
* let an application transfer SDOs before activating the master.
* - Removed 'const' from argument of ecrt_sdo_request_state(), because the
* userspace library has to modify object internals.
* - Added 64-bit data access macros.
@ -442,21 +445,23 @@ int ecrt_master_reserve(
/** Sets the locking callbacks.
*
* For concurrent master access, the application has to provide a locking
* mechanism (see section FIXME in the docs). The method takes two function
* pointers and a data value as its parameters. The arbitrary \a cb_data value
* will be passed as argument on every callback. Asynchronous master access
* (like EoE processing) is only possible if the callbacks have been set.
* For concurrent master access, i. e. if other instances than the application
* want to send and receive datagrams on the bus, the application has to
* provide a callback mechanism. This method takes two function pointers as
* its parameters. Asynchronous master access (like EoE processing) is only
* possible if the callbacks have been set.
*
* The request_cb function must return zero, to allow another instance
* (an EoE process for example) to access the master. Non-zero means,
* that access is currently forbidden.
* The task of the send callback (\a request_cb) is to decide, if the bus is
* currently accessible. In this case, it can call the ecrt_master_send_ext()
* method.
*
* The task of the receive callback (\a receive_cb) is to decide, if a call to
* ecrt_master_receive() is allowed and to execute it respectively.
*/
void ecrt_master_callbacks(
ec_master_t *master, /**< EtherCAT master */
int (*request_cb)(void *), /**< Lock request function. */
void (*release_cb)(void *), /**< Lock release function. */
void *cb_data /**< Arbitrary user data. */
void (*send_cb)(ec_master_t *), /**< Datagram sending callback. */
void (*receive_cb)(ec_master_t *) /**< Receive callback. */
);
#endif /* __KERNEL__ */
@ -591,6 +596,15 @@ void ecrt_master_receive(
ec_master_t *master /**< EtherCAT master. */
);
/** Sends non-application datagrams.
*
* This method has to be called in the send callback function passed via
* ecrt_master_callbacks() to allow the sending of non-application datagrams.
*/
void ecrt_master_send_ext(
ec_master_t *master /**< EtherCAT master. */
);
/** Reads the current master state.
*
* Stores the master state information in the given \a state structure.

View File

@ -1621,6 +1621,9 @@ int ec_cdev_ioctl_activate(
}
}
ecrt_master_callbacks(master, ec_master_internal_send_cb,
ec_master_internal_receive_cb);
ret = ecrt_master_activate(master);
if (ret < 0)
return ret;

View File

@ -324,7 +324,7 @@ void ec_eoe_run(ec_eoe_t *eoe /**< EoE handler */)
void ec_eoe_queue(ec_eoe_t *eoe /**< EoE handler */)
{
if (eoe->queue_datagram) {
ec_master_queue_datagram(eoe->slave->master, &eoe->datagram);
ec_master_queue_datagram_ext(eoe->slave->master, &eoe->datagram);
eoe->queue_datagram = 0;
}
}

View File

@ -146,6 +146,9 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
INIT_LIST_HEAD(&master->datagram_queue);
master->datagram_index = 0;
INIT_LIST_HEAD(&master->ext_datagram_queue);
init_MUTEX(&master->ext_queue_sem);
INIT_LIST_HEAD(&master->domains);
master->debug_level = debug_level;
@ -163,9 +166,10 @@ int ec_master_init(ec_master_t *master, /**< EtherCAT master */
#endif
init_MUTEX(&master->io_sem);
master->request_cb = NULL;
master->release_cb = NULL;
master->cb_data = NULL;
master->send_cb = NULL;
master->receive_cb = NULL;
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
INIT_LIST_HEAD(&master->sii_requests);
init_waitqueue_head(&master->sii_queue);
@ -375,22 +379,27 @@ void ec_master_clear_domains(ec_master_t *master)
/*****************************************************************************/
/** Internal locking callback.
/** Internal sending callback.
*/
int ec_master_request_cb(void *data /**< callback data */)
void ec_master_internal_send_cb(
ec_master_t *master /**< EtherCAT master. */
)
{
ec_master_t *master = (ec_master_t *) data;
down(&master->io_sem);
return 0;
ecrt_master_send_ext(master);
up(&master->io_sem);
}
/*****************************************************************************/
/** Internal unlocking callback.
/** Internal receiving callback.
*/
void ec_master_release_cb(void *data /**< callback data */)
void ec_master_internal_receive_cb(
ec_master_t *master /**< EtherCAT master. */
)
{
ec_master_t *master = (ec_master_t *) data;
down(&master->io_sem);
ecrt_master_receive(master);
up(&master->io_sem);
}
@ -462,9 +471,8 @@ int ec_master_enter_idle_phase(
if (master->debug_level)
EC_DBG("ORPHANED -> IDLE.\n");
master->request_cb = ec_master_request_cb;
master->release_cb = ec_master_release_cb;
master->cb_data = master;
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
master->phase = EC_IDLE;
ret = ec_master_thread_start(master, ec_master_idle_thread,
@ -564,9 +572,8 @@ int ec_master_enter_operation_phase(ec_master_t *master /**< EtherCAT master */)
#endif
master->phase = EC_OPERATION;
master->ext_request_cb = NULL;
master->ext_release_cb = NULL;
master->ext_cb_data = NULL;
master->app_send_cb = NULL;
master->app_receive_cb = NULL;
return ret;
out_allow:
@ -597,9 +604,8 @@ void ec_master_leave_operation_phase(ec_master_t *master
#endif
ec_master_thread_stop(master);
master->request_cb = ec_master_request_cb;
master->release_cb = ec_master_release_cb;
master->cb_data = master;
master->send_cb = ec_master_internal_send_cb;
master->receive_cb = ec_master_internal_receive_cb;
down(&master->master_sem);
ec_master_clear_domains(master);
@ -646,9 +652,10 @@ void ec_master_leave_operation_phase(ec_master_t *master
/** Places a datagram in the datagram queue.
*/
void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
void ec_master_queue_datagram(
ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
{
ec_datagram_t *queued_datagram;
@ -669,6 +676,20 @@ void ec_master_queue_datagram(ec_master_t *master, /**< EtherCAT master */
/*****************************************************************************/
/** Places a datagram in the non-application datagram queue.
*/
void ec_master_queue_datagram_ext(
ec_master_t *master, /**< EtherCAT master */
ec_datagram_t *datagram /**< datagram */
)
{
down(&master->ext_queue_sem);
list_add_tail(&datagram->queue, &master->ext_datagram_queue);
up(&master->ext_queue_sem);
}
/*****************************************************************************/
/** Sends the datagrams in the queue.
*
* \return 0 in case of success, else < 0
@ -1048,8 +1069,8 @@ void ec_master_eoe_start(ec_master_t *master /**< EtherCAT master */)
if (list_empty(&master->eoe_handlers))
return;
if (!master->request_cb || !master->release_cb) {
EC_WARN("No EoE processing because of missing locking callbacks!\n");
if (!master->send_cb || !master->receive_cb) {
EC_WARN("No EoE processing because of missing callbacks!\n");
return;
}
@ -1109,11 +1130,7 @@ static int ec_master_eoe_thread(void *priv_data)
goto schedule;
// receive datagrams
if (master->request_cb(master->cb_data))
goto schedule;
ecrt_master_receive(master);
master->release_cb(master->cb_data);
master->receive_cb(master);
// actual EoE processing
sth_to_send = 0;
@ -1128,15 +1145,13 @@ static int ec_master_eoe_thread(void *priv_data)
}
if (sth_to_send) {
// send datagrams
if (master->request_cb(master->cb_data)) {
goto schedule;
}
list_for_each_entry(eoe, &master->eoe_handlers, list) {
ec_eoe_queue(eoe);
}
ecrt_master_send(master);
master->release_cb(master->cb_data);
// (try to) send datagrams
down(&master->ext_queue_sem);
master->send_cb(master);
up(&master->ext_queue_sem);
}
schedule:
@ -1641,9 +1656,9 @@ int ecrt_master_activate(ec_master_t *master)
master->injection_seq_fsm = 0;
master->injection_seq_rt = 0;
master->request_cb = master->ext_request_cb;
master->release_cb = master->ext_release_cb;
master->cb_data = master->ext_cb_data;
master->send_cb = master->app_send_cb;
master->receive_cb = master->app_receive_cb;
ret = ec_master_thread_start(master, ec_master_operation_thread,
"EtherCAT-OP");
@ -1735,6 +1750,21 @@ void ecrt_master_receive(ec_master_t *master)
/*****************************************************************************/
void ecrt_master_send_ext(ec_master_t *master)
{
ec_datagram_t *datagram, *next;
list_for_each_entry_safe(datagram, next, &master->ext_datagram_queue,
queue) {
list_del(&datagram->queue);
ec_master_queue_datagram(master, datagram);
}
ecrt_master_send(master);
}
/*****************************************************************************/
/** Same as ecrt_master_slave_config(), but with ERR_PTR() return value.
*/
ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *master,
@ -1805,17 +1835,16 @@ ec_slave_config_t *ecrt_master_slave_config(ec_master_t *master,
/*****************************************************************************/
void ecrt_master_callbacks(ec_master_t *master, int (*request_cb)(void *),
void (*release_cb)(void *), void *cb_data)
void ecrt_master_callbacks(ec_master_t *master,
void (*send_cb)(ec_master_t *), void (*receive_cb)(ec_master_t *))
{
if (master->debug_level)
EC_DBG("ecrt_master_callbacks(master = 0x%x, request_cb = 0x%x, "
" release_cb = 0x%x, cb_data = 0x%x)\n", (u32) master,
(u32) request_cb, (u32) release_cb, (u32) cb_data);
EC_DBG("ecrt_master_callbacks(master = 0x%x, send_cb = 0x%x, "
" receive_cb = 0x%x)\n", (u32) master, (u32) send_cb,
(u32) receive_cb);
master->ext_request_cb = request_cb;
master->ext_release_cb = release_cb;
master->ext_cb_data = cb_data;
master->app_send_cb = send_cb;
master->app_receive_cb = receive_cb;
}
/*****************************************************************************/
@ -1862,6 +1891,7 @@ void ecrt_master_sync_slave_clocks(ec_master_t *master)
EXPORT_SYMBOL(ecrt_master_create_domain);
EXPORT_SYMBOL(ecrt_master_activate);
EXPORT_SYMBOL(ecrt_master_send);
EXPORT_SYMBOL(ecrt_master_send_ext);
EXPORT_SYMBOL(ecrt_master_receive);
EXPORT_SYMBOL(ecrt_master_callbacks);
EXPORT_SYMBOL(ecrt_master_slave_config);

View File

@ -145,6 +145,11 @@ struct ec_master {
struct list_head datagram_queue; /**< Datagram queue. */
uint8_t datagram_index; /**< Current datagram index. */
struct list_head ext_datagram_queue; /**< Queue for non-application
datagrams. */
struct semaphore ext_queue_sem; /**< Semaphore protecting the \a
ext_datagram_queue. */
struct list_head domains; /**< List of domains. */
unsigned int debug_level; /**< Master debug level. */
@ -160,12 +165,13 @@ struct ec_master {
#endif
struct semaphore io_sem; /**< Semaphore used in \a IDLE phase. */
int (*request_cb)(void *); /**< Lock request callback. */
void (*release_cb)(void *); /**< Lock release callback. */
void *cb_data; /**< Data parameter of locking callbacks. */
int (*ext_request_cb)(void *); /**< External lock request callback. */
void (*ext_release_cb)(void *); /**< External lock release callback. */
void *ext_cb_data; /**< Data parameter of external locking callbacks. */
void (*send_cb)(ec_master_t *); /**< Current send datagrams callback. */
void (*receive_cb)(ec_master_t *); /**< Current receive datagrams callback. */
void (*app_send_cb)(ec_master_t *); /**< Application's send datagrams
callback. */
void (*app_receive_cb)(ec_master_t *); /**< Application's receive datagrams
callback. */
struct list_head sii_requests; /**< SII write requests. */
wait_queue_head_t sii_queue; /**< Wait queue for SII
@ -208,6 +214,7 @@ void ec_master_eoe_stop(ec_master_t *);
// datagram IO
void ec_master_receive_datagrams(ec_master_t *, const uint8_t *, size_t);
void ec_master_queue_datagram(ec_master_t *, ec_datagram_t *);
void ec_master_queue_datagram_ext(ec_master_t *, ec_datagram_t *);
// misc.
void ec_master_attach_slave_configs(ec_master_t *);
@ -240,6 +247,9 @@ ec_slave_config_t *ecrt_master_slave_config_err(ec_master_t *, uint16_t,
void ec_master_calc_dc(ec_master_t *);
void ec_master_internal_send_cb(ec_master_t *);
void ec_master_internal_receive_cb(ec_master_t *);
/*****************************************************************************/
#endif