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