IgH EtherCAT Master  1.5.2
module.c
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * $Id$
4 *
5 * Copyright (C) 2006-2008 Florian Pose, Ingenieurgemeinschaft IgH
6 *
7 * This file is part of the IgH EtherCAT Master.
8 *
9 * The IgH EtherCAT Master is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License version 2, as
11 * published by the Free Software Foundation.
12 *
13 * The IgH EtherCAT Master is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
16 * Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with the IgH EtherCAT Master; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 *
22 * ---
23 *
24 * The license mentioned above concerns the source code only. Using the
25 * EtherCAT technology and brand is only permitted in compliance with the
26 * industrial property and similar rights of Beckhoff Automation GmbH.
27 *
28 *****************************************************************************/
29
34/*****************************************************************************/
35
36#include <linux/module.h>
37#include <linux/device.h>
38#include <linux/err.h>
39
40#include "globals.h"
41#include "master.h"
42#include "device.h"
43
44/*****************************************************************************/
45
46#define MAX_MASTERS 32
48/*****************************************************************************/
49
50int __init ec_init_module(void);
51void __exit ec_cleanup_module(void);
52
53static int ec_mac_parse(uint8_t *, const char *, int);
54
55/*****************************************************************************/
56
58static unsigned int master_count;
60static unsigned int backup_count;
61static unsigned int debug_level;
62static unsigned int run_on_cpu = 0xffffffff;
65static struct semaphore master_sem;
68struct class *class;
70static uint8_t macs[MAX_MASTERS][2][ETH_ALEN];
72char *ec_master_version_str = EC_MASTER_VERSION;
74/*****************************************************************************/
75
78MODULE_AUTHOR("Florian Pose <fp@igh-essen.com>");
79MODULE_DESCRIPTION("EtherCAT master driver module");
80MODULE_LICENSE("GPL");
81MODULE_VERSION(EC_MASTER_VERSION);
82
83module_param_array(main_devices, charp, &master_count, S_IRUGO);
84MODULE_PARM_DESC(main_devices, "MAC addresses of main devices");
85module_param_array(backup_devices, charp, &backup_count, S_IRUGO);
86MODULE_PARM_DESC(backup_devices, "MAC addresses of backup devices");
87module_param_named(debug_level, debug_level, uint, S_IRUGO);
88MODULE_PARM_DESC(debug_level, "Debug level");
89module_param_named(run_on_cpu, run_on_cpu, uint, S_IRUGO);
90MODULE_PARM_DESC(run_on_cpu, "Bind kthreads to a specific cpu");
91
94/*****************************************************************************/
95
101int __init ec_init_module(void)
102{
103 int i, ret = 0;
104
105 EC_INFO("Master driver %s\n", EC_MASTER_VERSION);
106
107 sema_init(&master_sem, 1);
108
109 if (master_count) {
110 if (alloc_chrdev_region(&device_number,
111 0, master_count, "EtherCAT")) {
112 EC_ERR("Failed to obtain device number(s)!\n");
113 ret = -EBUSY;
114 goto out_return;
115 }
116 }
117
118#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 4, 0)
119 class = class_create(THIS_MODULE, "EtherCAT");
120#else
121 class = class_create("EtherCAT");
122#endif
123 if (IS_ERR(class)) {
124 EC_ERR("Failed to create device class.\n");
125 ret = PTR_ERR(class);
126 goto out_cdev;
127 }
128
129 // zero MAC addresses
130 memset(macs, 0x00, sizeof(uint8_t) * MAX_MASTERS * 2 * ETH_ALEN);
131
132 // process MAC parameters
133 for (i = 0; i < master_count; i++) {
134 ret = ec_mac_parse(macs[i][0], main_devices[i], 0);
135 if (ret)
136 goto out_class;
137
138 if (i < backup_count) {
139 ret = ec_mac_parse(macs[i][1], backup_devices[i], 1);
140 if (ret)
141 goto out_class;
142 }
143 }
144
145 // initialize static master variables
147
148 if (master_count) {
149 if (!(masters = kmalloc(sizeof(ec_master_t) * master_count,
150 GFP_KERNEL))) {
151 EC_ERR("Failed to allocate memory"
152 " for EtherCAT masters.\n");
153 ret = -ENOMEM;
154 goto out_class;
155 }
156 }
157
158 for (i = 0; i < master_count; i++) {
159 ret = ec_master_init(&masters[i], i, macs[i][0], macs[i][1],
161 if (ret)
162 goto out_free_masters;
163 }
164
165 EC_INFO("%u master%s waiting for devices.\n",
166 master_count, (master_count == 1 ? "" : "s"));
167 return ret;
168
169out_free_masters:
170 for (i--; i >= 0; i--)
172 kfree(masters);
173out_class:
174 class_destroy(class);
175out_cdev:
176 if (master_count)
177 unregister_chrdev_region(device_number, master_count);
178out_return:
179 return ret;
180}
181
182/*****************************************************************************/
183
188void __exit ec_cleanup_module(void)
189{
190 unsigned int i;
191
192 for (i = 0; i < master_count; i++) {
194 }
195
196 if (master_count)
197 kfree(masters);
198
199 class_destroy(class);
200
201 if (master_count)
202 unregister_chrdev_region(device_number, master_count);
203
204 EC_INFO("Master module cleaned up.\n");
205}
206
207/*****************************************************************************/
208
211unsigned int ec_master_count(void)
212{
213 return master_count;
214}
215
216/*****************************************************************************
217 * MAC address functions
218 ****************************************************************************/
219
224 const uint8_t *mac1,
225 const uint8_t *mac2
226 )
227{
228 unsigned int i;
229
230 for (i = 0; i < ETH_ALEN; i++)
231 if (mac1[i] != mac2[i])
232 return 0;
233
234 return 1;
235}
236
237/*****************************************************************************/
238
241#define EC_MAX_MAC_STRING_SIZE (3 * ETH_ALEN)
242
250 const uint8_t *mac,
251 char *buffer
252 )
253{
254 off_t off = 0;
255 unsigned int i;
256
257 for (i = 0; i < ETH_ALEN; i++) {
258 off += sprintf(buffer + off, "%02X", mac[i]);
259 if (i < ETH_ALEN - 1) off += sprintf(buffer + off, ":");
260 }
261
262 return off;
263}
264
265/*****************************************************************************/
266
271 const uint8_t *mac
272 )
273{
274 unsigned int i;
275
276 for (i = 0; i < ETH_ALEN; i++)
277 if (mac[i])
278 return 0;
279
280 return 1;
281}
282
283/*****************************************************************************/
284
289 const uint8_t *mac
290 )
291{
292 unsigned int i;
293
294 for (i = 0; i < ETH_ALEN; i++)
295 if (mac[i] != 0xff)
296 return 0;
297
298 return 1;
299}
300
301/*****************************************************************************/
302
310static int ec_mac_parse(uint8_t *mac, const char *src, int allow_empty)
311{
312 unsigned int i, value;
313 const char *orig = src;
314 char *rem;
315
316 if (!strlen(src)) {
317 if (allow_empty){
318 return 0;
319 } else {
320 EC_ERR("MAC address may not be empty.\n");
321 return -EINVAL;
322 }
323 }
324
325 for (i = 0; i < ETH_ALEN; i++) {
326 value = simple_strtoul(src, &rem, 16);
327 if (rem != src + 2
328 || value > 0xFF
329 || (i < ETH_ALEN - 1 && *rem != ':')) {
330 EC_ERR("Invalid MAC address \"%s\".\n", orig);
331 return -EINVAL;
332 }
333 mac[i] = value;
334 if (i < ETH_ALEN - 1) {
335 src = rem + 1; // skip colon
336 }
337 }
338
339 return 0;
340}
341
342/*****************************************************************************/
343
348void ec_print_data(const uint8_t *data,
349 size_t size
350 )
351{
352 unsigned int i;
353
354 EC_DBG("");
355 for (i = 0; i < size; i++) {
356 printk(KERN_CONT "%02X ", data[i]);
357
358 if ((i + 1) % 16 == 0 && i < size - 1) {
359 printk(KERN_CONT "\n");
360 EC_DBG("");
361 }
362
363 if (i + 1 == 128 && size > 256) {
364 printk(KERN_CONT "dropped %zu bytes\n", size - 128 - i);
365 i = size - 128;
366 EC_DBG("");
367 }
368 }
369 printk(KERN_CONT "\n");
370}
371
372/*****************************************************************************/
373
376void ec_print_data_diff(const uint8_t *d1,
377 const uint8_t *d2,
378 size_t size
379 )
380{
381 unsigned int i;
382
383 EC_DBG("");
384 for (i = 0; i < size; i++) {
385 if (d1[i] == d2[i]) {
386 printk(KERN_CONT ".. ");
387 }
388 else {
389 printk(KERN_CONT "%02X ", d2[i]);
390 }
391 if ((i + 1) % 16 == 0) {
392 printk(KERN_CONT "\n");
393 EC_DBG("");
394 }
395 }
396 printk(KERN_CONT "\n");
397}
398
399/*****************************************************************************/
400
405size_t ec_state_string(uint8_t states,
406 char *buffer,
408 uint8_t multi
409 )
410{
411 off_t off = 0;
412 unsigned int first = 1;
413
414 if (!states) {
415 off += sprintf(buffer + off, "(unknown)");
416 return off;
417 }
418
419 if (multi) { // multiple slaves
420 if (states & EC_SLAVE_STATE_INIT) {
421 off += sprintf(buffer + off, "INIT");
422 first = 0;
423 }
424 if (states & EC_SLAVE_STATE_PREOP) {
425 if (!first) off += sprintf(buffer + off, ", ");
426 off += sprintf(buffer + off, "PREOP");
427 first = 0;
428 }
429 if (states & EC_SLAVE_STATE_SAFEOP) {
430 if (!first) off += sprintf(buffer + off, ", ");
431 off += sprintf(buffer + off, "SAFEOP");
432 first = 0;
433 }
434 if (states & EC_SLAVE_STATE_OP) {
435 if (!first) off += sprintf(buffer + off, ", ");
436 off += sprintf(buffer + off, "OP");
437 }
438 } else { // single slave
439 if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_INIT) {
440 off += sprintf(buffer + off, "INIT");
441 } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_PREOP) {
442 off += sprintf(buffer + off, "PREOP");
443 } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_BOOT) {
444 off += sprintf(buffer + off, "BOOT");
445 } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_SAFEOP) {
446 off += sprintf(buffer + off, "SAFEOP");
447 } else if ((states & EC_SLAVE_STATE_MASK) == EC_SLAVE_STATE_OP) {
448 off += sprintf(buffer + off, "OP");
449 } else {
450 off += sprintf(buffer + off, "(invalid)");
451 }
452 first = 0;
453 }
454
455 if (states & EC_SLAVE_STATE_ACK_ERR) {
456 if (!first) off += sprintf(buffer + off, " + ");
457 off += sprintf(buffer + off, "ERROR");
458 }
459
460 return off;
461}
462
463/******************************************************************************
464 * Device interface
465 *****************************************************************************/
466
469const char *ec_device_names[2] = {
470 "main",
471 "backup"
472};
473
485 struct net_device *net_dev,
486 ec_pollfunc_t poll,
487 struct module *module
488 )
489{
490 ec_master_t *master;
491 char str[EC_MAX_MAC_STRING_SIZE];
492 unsigned int i, dev_idx;
493
494 for (i = 0; i < master_count; i++) {
495 master = &masters[i];
496 ec_mac_print(net_dev->dev_addr, str);
497
498 if (down_interruptible(&master->device_sem)) {
499 EC_MASTER_WARN(master, "%s() interrupted!\n", __func__);
500 return NULL;
501 }
502
503 for (dev_idx = EC_DEVICE_MAIN;
504 dev_idx < ec_master_num_devices(master); dev_idx++) {
505 if (!master->devices[dev_idx].dev
506 && (ec_mac_equal(master->macs[dev_idx], net_dev->dev_addr)
507 || ec_mac_is_broadcast(master->macs[dev_idx]))) {
508
509 EC_INFO("Accepting %s as %s device for master %u.\n",
510 str, ec_device_names[dev_idx != 0], master->index);
511
512 ec_device_attach(&master->devices[dev_idx],
513 net_dev, poll, module);
514 up(&master->device_sem);
515
516 snprintf(net_dev->name, IFNAMSIZ, "ec%c%u",
517 ec_device_names[dev_idx != 0][0], master->index);
518
519 return &master->devices[dev_idx]; // offer accepted
520 }
521 }
522
523 up(&master->device_sem);
524
525 EC_MASTER_DBG(master, 1, "Master declined device %s.\n", str);
526 }
527
528 return NULL; // offer declined
529}
530
531/******************************************************************************
532 * Application interface
533 *****************************************************************************/
534
542 unsigned int master_index
543 )
544{
545 ec_master_t *master, *errptr = NULL;
546 unsigned int dev_idx = EC_DEVICE_MAIN;
547
548 EC_INFO("Requesting master %u...\n", master_index);
549
550 if (master_index >= master_count) {
551 EC_ERR("Invalid master index %u.\n", master_index);
552 errptr = ERR_PTR(-EINVAL);
553 goto out_return;
554 }
555 master = &masters[master_index];
556
557 if (down_interruptible(&master_sem)) {
558 errptr = ERR_PTR(-EINTR);
559 goto out_return;
560 }
561
562 if (master->reserved) {
563 up(&master_sem);
564 EC_MASTER_ERR(master, "Master already in use!\n");
565 errptr = ERR_PTR(-EBUSY);
566 goto out_return;
567 }
568 master->reserved = 1;
569 up(&master_sem);
570
571 if (down_interruptible(&master->device_sem)) {
572 errptr = ERR_PTR(-EINTR);
573 goto out_release;
574 }
575
576 if (master->phase != EC_IDLE) {
577 up(&master->device_sem);
578 EC_MASTER_ERR(master, "Master still waiting for devices!\n");
579 errptr = ERR_PTR(-ENODEV);
580 goto out_release;
581 }
582
583 for (; dev_idx < ec_master_num_devices(master); dev_idx++) {
584 ec_device_t *device = &master->devices[dev_idx];
585 if (!try_module_get(device->module)) {
586 up(&master->device_sem);
587 EC_MASTER_ERR(master, "Device module is unloading!\n");
588 errptr = ERR_PTR(-ENODEV);
589 goto out_module_put;
590 }
591 }
592
593 up(&master->device_sem);
594
596 EC_MASTER_ERR(master, "Failed to enter OPERATION phase!\n");
597 errptr = ERR_PTR(-EIO);
598 goto out_module_put;
599 }
600
601 EC_INFO("Successfully requested master %u.\n", master_index);
602 return master;
603
604 out_module_put:
605 for (; dev_idx > 0; dev_idx--) {
606 ec_device_t *device = &master->devices[dev_idx - 1];
607 module_put(device->module);
608 }
609 out_release:
610 master->reserved = 0;
611 out_return:
612 return errptr;
613}
614
615/*****************************************************************************/
616
617ec_master_t *ecrt_request_master(unsigned int master_index)
618{
619 ec_master_t *master = ecrt_request_master_err(master_index);
620 return IS_ERR(master) ? NULL : master;
621}
622
623/*****************************************************************************/
624
626{
627 unsigned int dev_idx;
628
629 EC_MASTER_INFO(master, "Releasing master...\n");
630
631 if (!master->reserved) {
632 EC_MASTER_WARN(master, "%s(): Master was was not requested!\n",
633 __func__);
634 return;
635 }
636
638
639 for (dev_idx = EC_DEVICE_MAIN; dev_idx < ec_master_num_devices(master);
640 dev_idx++) {
641 module_put(master->devices[dev_idx].module);
642 }
643
644 master->reserved = 0;
645
646 EC_MASTER_INFO(master, "Released.\n");
647}
648
649/*****************************************************************************/
650
651unsigned int ecrt_version_magic(void)
652{
653 return ECRT_VERSION_MAGIC;
654}
655
656/*****************************************************************************/
657
663 EC_REQUEST_UNUSED, // EC_INT_REQUEST_INIT,
664 EC_REQUEST_BUSY, // EC_INT_REQUEST_QUEUED,
665 EC_REQUEST_BUSY, // EC_INT_REQUEST_BUSY,
666 EC_REQUEST_SUCCESS, // EC_INT_REQUEST_SUCCESS,
667 EC_REQUEST_ERROR // EC_INT_REQUEST_FAILURE
668};
669
670/*****************************************************************************/
671
674module_init(ec_init_module);
675module_exit(ec_cleanup_module);
676
677EXPORT_SYMBOL(ecdev_offer);
678
679EXPORT_SYMBOL(ecrt_request_master);
680EXPORT_SYMBOL(ecrt_release_master);
681EXPORT_SYMBOL(ecrt_version_magic);
682
685/*****************************************************************************/
void ec_device_attach(ec_device_t *device, struct net_device *net_dev, ec_pollfunc_t poll, struct module *module)
Associate with net_device.
Definition: device.c:182
EtherCAT device structure.
void(* ec_pollfunc_t)(struct net_device *)
Device poll function type.
Definition: ecdev.h:57
Global definitions and macros.
@ EC_SLAVE_STATE_BOOT
Bootstrap state (mailbox communication, firmware update)
Definition: globals.h:122
@ EC_SLAVE_STATE_PREOP
PREOP state (mailbox communication, no IO)
Definition: globals.h:120
@ EC_SLAVE_STATE_OP
OP (mailbox communication and input/output update)
Definition: globals.h:126
@ EC_SLAVE_STATE_SAFEOP
SAFEOP (mailbox communication and input update)
Definition: globals.h:124
@ EC_SLAVE_STATE_INIT
INIT state (no mailbox communication, no IO)
Definition: globals.h:118
@ EC_SLAVE_STATE_ACK_ERR
Acknowledge/Error bit (no actual state)
Definition: globals.h:128
#define EC_SLAVE_STATE_MASK
Slave state mask.
Definition: globals.h:111
@ EC_DEVICE_MAIN
Main device.
Definition: globals.h:190
#define EC_ERR(fmt, args...)
Convenience macro for printing EtherCAT-specific errors to syslog.
Definition: globals.h:215
#define EC_DBG(fmt, args...)
Convenience macro for printing EtherCAT debug messages to syslog.
Definition: globals.h:235
#define EC_INFO(fmt, args...)
Convenience macro for printing EtherCAT-specific information to syslog.
Definition: globals.h:205
unsigned int ecrt_version_magic(void)
Returns the version magic of the realtime interface.
Definition: module.c:651
ec_master_t * ecrt_request_master(unsigned int master_index)
Requests an EtherCAT master for realtime operation.
Definition: module.c:617
void ecrt_release_master(ec_master_t *master)
Releases a requested EtherCAT master.
Definition: module.c:625
#define ECRT_VERSION_MAGIC
EtherCAT realtime interface version word.
Definition: ecrt.h:156
ec_request_state_t
Request state.
Definition: ecrt.h:530
@ EC_REQUEST_ERROR
Request processing failed.
Definition: ecrt.h:534
@ EC_REQUEST_UNUSED
Not requested.
Definition: ecrt.h:531
@ EC_REQUEST_SUCCESS
Request was processed successfully.
Definition: ecrt.h:533
@ EC_REQUEST_BUSY
Request is being processed.
Definition: ecrt.h:532
ec_device_t * ecdev_offer(struct net_device *net_dev, ec_pollfunc_t poll, struct module *module)
Offers an EtherCAT device to a certain master.
Definition: module.c:484
void ec_master_init_static(void)
Static variables initializer.
Definition: master.c:121
int ec_master_init(ec_master_t *master, unsigned int index, const uint8_t *main_mac, const uint8_t *backup_mac, dev_t device_number, struct class *class, unsigned int debug_level, unsigned int run_on_cpu)
Master constructor.
Definition: master.c:142
int ec_master_enter_operation_phase(ec_master_t *master)
Transition function from IDLE to OPERATION phase.
Definition: master.c:698
void ec_master_clear(ec_master_t *master)
Destructor.
Definition: master.c:395
void ec_master_leave_operation_phase(ec_master_t *master)
Transition function from OPERATION to IDLE phase.
Definition: master.c:780
EtherCAT master structure.
#define ec_master_num_devices(MASTER)
Number of Ethernet devices.
Definition: master.h:330
#define EC_MASTER_INFO(master, fmt, args...)
Convenience macro for printing master-specific information to syslog.
Definition: master.h:73
#define EC_MASTER_DBG(master, level, fmt, args...)
Convenience macro for printing master-specific debug messages to syslog.
Definition: master.h:111
@ EC_IDLE
Idle phase.
Definition: master.h:133
#define EC_MASTER_ERR(master, fmt, args...)
Convenience macro for printing master-specific errors to syslog.
Definition: master.h:85
#define EC_MASTER_WARN(master, fmt, args...)
Convenience macro for printing master-specific warnings to syslog.
Definition: master.h:97
dev_t device_number
Device number for master cdevs.
Definition: module.c:67
size_t ec_state_string(uint8_t states, char *buffer, uint8_t multi)
Prints slave states in clear text.
Definition: module.c:405
static int ec_mac_parse(uint8_t *, const char *, int)
Parse a MAC address from a string.
Definition: module.c:310
void ec_print_data(const uint8_t *data, size_t size)
Outputs frame contents for debugging purposes.
Definition: module.c:348
#define EC_MAX_MAC_STRING_SIZE
Maximum MAC string size.
Definition: module.c:241
const ec_request_state_t ec_request_state_translation_table[]
Global request state type translation table.
Definition: module.c:662
unsigned int ec_master_count(void)
Get the number of masters.
Definition: module.c:211
static char * main_devices[MAX_MASTERS]
Main devices parameter.
Definition: module.c:57
ssize_t ec_mac_print(const uint8_t *mac, char *buffer)
Print a MAC address to a buffer.
Definition: module.c:249
void ec_print_data_diff(const uint8_t *d1, const uint8_t *d2, size_t size)
Outputs frame contents and differences for debugging purposes.
Definition: module.c:376
static unsigned int run_on_cpu
Bind created kernel threads to a cpu.
Definition: module.c:62
#define MAX_MASTERS
Maximum number of masters.
Definition: module.c:46
int ec_mac_is_zero(const uint8_t *mac)
Definition: module.c:270
int ec_mac_is_broadcast(const uint8_t *mac)
Definition: module.c:288
static unsigned int backup_count
Number of backup devices.
Definition: module.c:60
static uint8_t macs[MAX_MASTERS][2][ETH_ALEN]
MAC addresses.
Definition: module.c:70
struct class * class
Device class.
Definition: module.c:68
static unsigned int master_count
Number of masters.
Definition: module.c:58
static ec_master_t * masters
Array of masters.
Definition: module.c:64
static unsigned int debug_level
Debug level parameter.
Definition: module.c:61
char * ec_master_version_str
Version string.
Definition: module.c:72
const char * ec_device_names[2]
Device names.
Definition: module.c:469
void __exit ec_cleanup_module(void)
Module cleanup.
Definition: module.c:188
int __init ec_init_module(void)
Module initialization.
Definition: module.c:101
static struct semaphore master_sem
Master semaphore.
Definition: module.c:65
ec_master_t * ecrt_request_master_err(unsigned int master_index)
Request a master.
Definition: module.c:541
static char * backup_devices[MAX_MASTERS]
Backup devices parameter.
Definition: module.c:59
int ec_mac_equal(const uint8_t *mac1, const uint8_t *mac2)
Definition: module.c:223
EtherCAT device.
Definition: device.h:82
struct module * module
pointer to the device's owning module
Definition: device.h:86
struct net_device * dev
pointer to the assigned net_device
Definition: device.h:84
EtherCAT master.
Definition: master.h:194
unsigned int index
Index.
Definition: master.h:195
struct semaphore device_sem
Device semaphore.
Definition: master.h:218
unsigned int reserved
True, if the master is in use.
Definition: master.h:196
const uint8_t * macs[EC_MAX_NUM_DEVICES]
Device MAC addresses.
Definition: master.h:212
ec_master_phase_t phase
Master phase.
Definition: master.h:223
ec_device_t devices[EC_MAX_NUM_DEVICES]
EtherCAT devices.
Definition: master.h:211