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