LIRC libraries
Linux Infrared Remote Control
Loading...
Searching...
No Matches
config_file.c
Go to the documentation of this file.
1/****************************************************************************
2** config_file.c ***********************************************************
3****************************************************************************
4*
5*
6* Copyright (C) 1998 Pablo d'Angelo <pablo@ag-trek.allgaeu.org>
7*
8*/
9
17#ifdef HAVE_CONFIG_H
18# include <config.h>
19#endif
20
21#include <dirent.h>
22#include <errno.h>
23#include <glob.h>
24#include <limits.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdint.h>
28#include <stdlib.h>
29#include <string.h>
30#include <libgen.h>
31#include <sys/socket.h>
32#include <sys/un.h>
33#include <sys/stat.h>
34#include <sys/types.h>
35#include <fcntl.h>
36#include <ctype.h>
37
38#include "media/lirc.h"
39
40#include "lirc/lirc_log.h"
41#include "lirc/lirc_options.h"
42#include "lirc/ir_remote.h"
43#include "lirc/config_file.h"
44#include "lirc/transmit.h"
45#include "lirc/config_flags.h"
46
47
48static const logchannel_t logchannel = LOG_LIB;
49
50enum directive { ID_none, ID_remote, ID_codes, ID_raw_codes, ID_raw_name };
51
52struct ptr_array {
53 void** ptr;
54 size_t nr_items;
55 size_t chunk_size;
56};
57
58struct void_array {
59 void* ptr;
60 size_t item_size;
61 size_t nr_items;
62 size_t chunk_size;
63};
64
65
67typedef void* (*array_guest_func)(void* item, void* arg);
68
69
70#define LINE_LEN 4096
71#define MAX_INCLUDES 10
72
73const char* whitespace = " \t";
74
75static int line;
76static int parse_error;
77
78static struct ir_remote* read_config_recursive(FILE* f, const char* name, int depth);
79static void calculate_signal_lengths(struct ir_remote* remote);
80
81void** init_void_array(struct void_array* ar, size_t chunk_size, size_t item_size)
82{
83 ar->chunk_size = chunk_size;
84 ar->item_size = item_size;
85 ar->nr_items = 0;
86 ar->ptr = calloc(chunk_size, ar->item_size);
87 if (!ar->ptr) {
88 log_error("out of memory");
89 parse_error = 1;
90 return NULL;
91 }
92 return ar->ptr;
93}
94
95const struct flaglist all_flags[] = {
96 { "RAW_CODES", RAW_CODES },
97 { "RC5", RC5 },
98 { "SHIFT_ENC", SHIFT_ENC }, /* obsolete */
99 { "RC6", RC6 },
100 { "RCMM", RCMM },
101 { "SPACE_ENC", SPACE_ENC },
102 { "SPACE_FIRST", SPACE_FIRST },
103 { "GRUNDIG", GRUNDIG },
104 { "BO", BO },
105 { "SERIAL", SERIAL },
106 { "XMP", XMP },
107
108 { "REVERSE", REVERSE },
109 { "NO_HEAD_REP", NO_HEAD_REP },
110 { "NO_FOOT_REP", NO_FOOT_REP },
111 { "CONST_LENGTH", CONST_LENGTH }, /* remember to adapt warning
112 * message when changing this */
113 { "REPEAT_HEADER", REPEAT_HEADER },
114 { NULL, 0 },
115};
116
117
119int add_void_array(struct void_array* ar, void* dataptr)
120{
121 void* ptr;
122
123 if ((ar->nr_items % ar->chunk_size) == (ar->chunk_size) - 1) {
124 /* I hope this works with the right alignment,
125 * if not we're screwed */
126 ptr = realloc(ar->ptr,
127 ar->item_size *
128 (ar->nr_items + ar->chunk_size + 1));
129 if (!ptr) {
130 log_error("out of memory");
131 parse_error = 1;
132 return 0;
133 }
134 ar->ptr = ptr;
135 }
136 memcpy((ar->ptr) + (ar->item_size * ar->nr_items), dataptr, ar->item_size);
137 ar->nr_items = (ar->nr_items) + 1;
138 memset((ar->ptr) + (ar->item_size * ar->nr_items), 0, ar->item_size);
139 return 1;
140}
141
142
144void* get_void_array(struct void_array* ar)
145{
146 return ar->ptr;
147}
148
149
154static void*
155foreach_void_array(struct void_array* ar, array_guest_func func, void* arg)
156{
157 void* r;
158 int i;
159
160 for (i = 0; i < ar->nr_items; i += 1) {
161 r = func(ar->ptr + (i * ar->item_size), arg);
162 if (r != NULL)
163 return r;
164 }
165 return NULL;
166}
167
168
169static int
170ir_code_node_equals(struct ir_code_node* node1, struct ir_code_node* node2)
171{
172 if (node1 == NULL || node2 == NULL)
173 return node1 == node2;
174 return node1->code == node2->code;
175}
176
177
182static void* array_guest_code_equals(void* arg1, void* arg2)
183{
184
185 struct ir_ncode* code1 = (struct ir_ncode*) arg1;
186 struct ir_ncode* code2 = (struct ir_ncode*) arg2;
187 struct ir_code_node* next1;
188 struct ir_code_node* next2;
189
190 if (code1 == NULL || code2 == NULL)
191 return NULL;
192 if (code1->code != code2->code)
193 return NULL;
194 next1 = code1->next;
195 next2 = code2->next;
196 while (next1 != NULL) {
197 if (!ir_code_node_equals(next1, next2))
198 return NULL;
199 next1 = next1->next;
200 next2 = next2->next;
201 }
202 return next2 == NULL ? arg1 : NULL;
203}
204
205
210static void* array_guest_ncode_cmp(void* item, void* arg)
211{
212
213 struct ir_ncode* code1 = (struct ir_ncode*) item;
214 struct ir_ncode* code2 = (struct ir_ncode*) arg;
215
216 if (strcmp(code1->name, code2->name) == 0)
217 return item;
218 return NULL;
219}
220
221
222void* s_malloc(size_t size)
223{
224 void* ptr;
225
226 ptr = malloc(size);
227 if (ptr == NULL) {
228 log_error("out of memory");
229 parse_error = 1;
230 return NULL;
231 }
232 memset(ptr, 0, size);
233 return ptr;
234}
235
236char* s_strdup(char* string)
237{
238 char* ptr;
239
240 ptr = strdup(string);
241 if (!ptr) {
242 log_error("out of memory");
243 parse_error = 1;
244 return NULL;
245 }
246 return ptr;
247}
248
249ir_code s_strtocode(const char* val)
250{
251 ir_code code = 0;
252 char* endptr;
253
254 errno = 0;
255 code = strtoull(val, &endptr, 0);
256 if ((code == (uint64_t) -1 && errno == ERANGE) || strlen(endptr) != 0 || strlen(val) == 0) {
257 log_error("error in configfile line %d:", line);
258 log_error("\"%s\": must be a valid (uint64_t) number", val);
259 parse_error = 1;
260 return 0;
261 }
262 return code;
263}
264
265uint32_t s_strtou32(char* val)
266{
267 uint32_t n;
268 char* endptr;
269
270 n = strtoul(val, &endptr, 0);
271 if (!*val || *endptr) {
272 log_error("error in configfile line %d:", line);
273 log_error("\"%s\": must be a valid (uint32_t) number", val);
274 parse_error = 1;
275 return 0;
276 }
277 return n;
278}
279
280int s_strtoi(char* val)
281{
282 char* endptr;
283 long n;
284 int h;
285
286 n = strtol(val, &endptr, 0);
287 h = (int)n;
288 if (!*val || *endptr || n != ((long)h)) {
289 log_error("error in configfile line %d:", line);
290 log_error("\"%s\": must be a valid (int) number", val);
291 parse_error = 1;
292 return 0;
293 }
294 return h;
295}
296
297unsigned int s_strtoui(char* val)
298{
299 char* endptr;
300 uint32_t n;
301 unsigned int h;
302
303 n = strtoul(val, &endptr, 0);
304 h = (unsigned int)n;
305 if (!*val || *endptr || n != ((uint32_t)h)) {
306 log_error("error in configfile line %d:", line);
307 log_error("\"%s\": must be a valid (unsigned int) number", val);
308 parse_error = 1;
309 return 0;
310 }
311 return h;
312}
313
314lirc_t s_strtolirc_t(char* val)
315{
316 uint32_t n;
317 lirc_t h;
318 char* endptr;
319
320 n = strtoul(val, &endptr, 0);
321 h = (lirc_t)n;
322 if (!*val || *endptr || n != ((uint32_t)h)) {
323 log_error("error in configfile line %d:", line);
324 log_error("\"%s\": must be a valid (lirc_t) number", val);
325 parse_error = 1;
326 return 0;
327 }
328 if (h < 0) {
329 log_warn("error in configfile line %d:", line);
330 log_warn("\"%s\" is out of range", val);
331 }
332 return h;
333}
334
335int checkMode(int is_mode, int c_mode, char* error)
336{
337 if (is_mode != c_mode) {
338 log_error("fatal error in configfile line %d:", line);
339 log_error("\"%s\" isn't valid at this position", error);
340 parse_error = 1;
341 return 0;
342 }
343 return 1;
344}
345
346int addSignal(struct void_array* signals, char* val)
347{
348 unsigned int t;
349
350 t = s_strtoui(val);
351 if (parse_error)
352 return 0;
353 if (!add_void_array(signals, &t))
354 return 0;
355
356 return 1;
357}
358
359struct ir_ncode* defineCode(char* key, char* val, struct ir_ncode* code)
360{
361 memset(code, 0, sizeof(*code));
362 code->name = s_strdup(key);
363 code->code = s_strtocode(val);
364 log_trace2(" %-20s 0x%016llX", code->name, code->code);
365 return code;
366}
367
368struct ir_code_node* defineNode(struct ir_ncode* code, const char* val)
369{
370 struct ir_code_node* node;
371
372 node = s_malloc(sizeof(*node));
373 if (node == NULL)
374 return NULL;
375
376 node->code = s_strtocode(val);
377 node->next = NULL;
378
379 log_trace2(" 0x%016llX", node->code);
380
381 if (code->current == NULL) {
382 code->next = node;
383 code->current = node;
384 } else {
385 code->current->next = node;
386 code->current = node;
387 }
388 return node;
389}
390
391int parseFlags(char* val)
392{
393 const struct flaglist* flaglptr;
394 int flags = 0;
395 char* flag;
396 char* help;
397
398 flag = help = val;
399 while (flag != NULL) {
400 while (*help != '|' && *help != 0)
401 help++;
402 if (*help == '|') {
403 *help = 0;
404 help++;
405 } else {
406 help = NULL;
407 }
408
409 flaglptr = all_flags;
410 while (flaglptr->name != NULL) {
411 if (strcasecmp(flaglptr->name, flag) == 0) {
412 if (flaglptr->flag & IR_PROTOCOL_MASK && flags & IR_PROTOCOL_MASK) {
413 log_error("error in configfile line %d:", line);
414 log_error("multiple protocols given in flags: \"%s\"", flag);
415 parse_error = 1;
416 return 0;
417 }
418 flags = flags | flaglptr->flag;
419 log_trace2("flag %s recognized", flaglptr->name);
420 break;
421 }
422 flaglptr++;
423 }
424 if (flaglptr->name == NULL) {
425 log_error("error in configfile line %d:", line);
426 log_error("unknown flag: \"%s\"", flag);
427 parse_error = 1;
428 return 0;
429 }
430 flag = help;
431 }
432 log_trace1("flags value: %d", flags);
433
434 return flags;
435}
436
437int defineRemote(char* key, char* val, char* val2, struct ir_remote* rem)
438{
439 if ((strcasecmp("name", key)) == 0) {
440 if (rem->name != NULL)
441 free((void*)(rem->name));
442 rem->name = s_strdup(val);
443 log_info("Using remote: %s.", val);
444 return 1;
445 }
446 if (options_getboolean("lircd:dynamic-codes")) {
447 if ((strcasecmp("dyncodes_name", key)) == 0) {
448 if (rem->dyncodes_name != NULL)
449 free(rem->dyncodes_name);
450 rem->dyncodes_name = s_strdup(val);
451 return 1;
452 }
453 } else if (strcasecmp("driver", key) == 0) {
454 if (rem->driver != NULL)
455 free((void*)(rem->driver));
456 rem->driver = s_strdup(val);
457 return 1;
458 } else if ((strcasecmp("bits", key)) == 0) {
459 rem->bits = s_strtoi(val);
460 return 1;
461 } else if (strcasecmp("flags", key) == 0) {
462 rem->flags |= parseFlags(val);
463 return 1;
464 } else if (strcasecmp("eps", key) == 0) {
465 rem->eps = s_strtoi(val);
466 return 1;
467 } else if (strcasecmp("aeps", key) == 0) {
468 rem->aeps = s_strtoi(val);
469 return 1;
470 } else if (strcasecmp("plead", key) == 0) {
471 rem->plead = s_strtolirc_t(val);
472 return 1;
473 } else if (strcasecmp("ptrail", key) == 0) {
474 rem->ptrail = s_strtolirc_t(val);
475 return 1;
476 } else if (strcasecmp("pre_data_bits", key) == 0) {
477 rem->pre_data_bits = s_strtoi(val);
478 return 1;
479 } else if (strcasecmp("pre_data", key) == 0) {
480 rem->pre_data = s_strtocode(val);
481 return 1;
482 } else if (strcasecmp("post_data_bits", key) == 0) {
483 rem->post_data_bits = s_strtoi(val);
484 return 1;
485 } else if (strcasecmp("post_data", key) == 0) {
486 rem->post_data = s_strtocode(val);
487 return 1;
488 } else if (strcasecmp("gap", key) == 0) {
489 if (val2 != NULL)
490 rem->gap2 = s_strtou32(val2);
491 rem->gap = s_strtou32(val);
492 return val2 != NULL ? 2 : 1;
493 } else if (strcasecmp("repeat_gap", key) == 0) {
494 rem->repeat_gap = s_strtou32(val);
495 return 1;
496 } else if (strcasecmp("repeat_mask", key) == 0) {
497 rem->repeat_mask = s_strtocode(val);
498 return 1;
499 }
500 /* obsolete: use toggle_bit_mask instead */
501 else if (strcasecmp("toggle_bit", key) == 0) {
502 rem->toggle_bit = s_strtoi(val);
503 return 1;
504 } else if (strcasecmp("toggle_bit_mask", key) == 0) {
505 rem->toggle_bit_mask = s_strtocode(val);
506 return 1;
507 } else if (strcasecmp("toggle_mask", key) == 0) {
508 rem->toggle_mask = s_strtocode(val);
509 return 1;
510 } else if (strcasecmp("rc6_mask", key) == 0) {
511 rem->rc6_mask = s_strtocode(val);
512 return 1;
513 } else if (strcasecmp("ignore_mask", key) == 0) {
514 rem->ignore_mask = s_strtocode(val);
515 return 1;
516 } else if (strcasecmp("manual_sort", key) == 0) {
517 rem->manual_sort = s_strtoi(val);
518 return 1;
519 }
520 /* obsolete name */
521 else if (strcasecmp("repeat_bit", key) == 0) {
522 rem->toggle_bit = s_strtoi(val);
523 return 1;
524 } else if (strcasecmp("suppress_repeat", key) == 0) {
525 rem->suppress_repeat = s_strtoi(val);
526 return 1;
527 } else if (strcasecmp("min_repeat", key) == 0) {
528 rem->min_repeat = s_strtoi(val);
529 return 1;
530 } else if (strcasecmp("min_code_repeat", key) == 0) {
531 rem->min_code_repeat = s_strtoi(val);
532 return 1;
533 } else if (strcasecmp("frequency", key) == 0) {
534 rem->freq = s_strtoui(val);
535 return 1;
536 } else if (strcasecmp("duty_cycle", key) == 0) {
537 rem->duty_cycle = s_strtoui(val);
538 return 1;
539 } else if (strcasecmp("baud", key) == 0) {
540 rem->baud = s_strtoui(val);
541 return 1;
542 } else if (strcasecmp("serial_mode", key) == 0) {
543 if (val[0] < '5' || val[0] > '9') {
544 log_error("error in configfile line %d:", line);
545 log_error("bad bit count");
546 parse_error = 1;
547 return 0;
548 }
549 rem->bits_in_byte = val[0] - '0';
550 switch (toupper(val[1])) {
551 case 'N':
552 rem->parity = IR_PARITY_NONE;
553 break;
554 case 'E':
555 rem->parity = IR_PARITY_EVEN;
556 break;
557 case 'O':
558 rem->parity = IR_PARITY_ODD;
559 break;
560 default:
561 log_error("error in configfile line %d:", line);
562 log_error("unsupported parity mode");
563 parse_error = 1;
564 return 0;
565 }
566 if (strcmp(val + 2, "1.5") == 0)
567 rem->stop_bits = 3;
568 else
569 rem->stop_bits = s_strtoui(val + 2) * 2;
570 return 1;
571 } else if (val2 != NULL) {
572 if (strcasecmp("header", key) == 0) {
573 rem->phead = s_strtolirc_t(val);
574 rem->shead = s_strtolirc_t(val2);
575 return 2;
576 } else if (strcasecmp("three", key) == 0) {
577 rem->pthree = s_strtolirc_t(val);
578 rem->sthree = s_strtolirc_t(val2);
579 return 2;
580 } else if (strcasecmp("two", key) == 0) {
581 rem->ptwo = s_strtolirc_t(val);
582 rem->stwo = s_strtolirc_t(val2);
583 return 2;
584 } else if (strcasecmp("one", key) == 0) {
585 rem->pone = s_strtolirc_t(val);
586 rem->sone = s_strtolirc_t(val2);
587 return 2;
588 } else if (strcasecmp("zero", key) == 0) {
589 rem->pzero = s_strtolirc_t(val);
590 rem->szero = s_strtolirc_t(val2);
591 return 2;
592 } else if (strcasecmp("foot", key) == 0) {
593 rem->pfoot = s_strtolirc_t(val);
594 rem->sfoot = s_strtolirc_t(val2);
595 return 2;
596 } else if (strcasecmp("repeat", key) == 0) {
597 rem->prepeat = s_strtolirc_t(val);
598 rem->srepeat = s_strtolirc_t(val2);
599 return 2;
600 } else if (strcasecmp("pre", key) == 0) {
601 rem->pre_p = s_strtolirc_t(val);
602 rem->pre_s = s_strtolirc_t(val2);
603 return 2;
604 } else if (strcasecmp("post", key) == 0) {
605 rem->post_p = s_strtolirc_t(val);
606 rem->post_s = s_strtolirc_t(val2);
607 return 2;
608 }
609 }
610 if (val2) {
611 log_error("error in configfile line %d:", line);
612 log_error("unknown definiton: \"%s %s %s\"", key, val, val2);
613 } else {
614 log_error("error in configfile line %d:", line);
615 log_error("unknown definiton or too few arguments: \"%s %s\"", key, val);
616 }
617 parse_error = 1;
618 return 0;
619}
620
621static int sanityChecks(struct ir_remote* rem, const char* path)
622{
623 struct ir_ncode* codes;
624 struct ir_code_node* node;
625
626 path = path != NULL ? path : "unknown file";
627
628 if (!rem->name) {
629 log_error("%s: Missing remote name", path);
630 return 0;
631 }
632 if (rem->gap == 0) {
633 log_warn("%s: %s: Gap value missing or invalid",
634 path, rem->name);
635 }
636 if (has_repeat_gap(rem) && is_const(rem)) {
637 log_warn("%s: %s: Repeat_gap ignored (CONST_LENGTH is set)",
638 path, rem->name);
639 }
640
641 if (is_raw(rem))
642 return 1;
643
644 if ((rem->pre_data & gen_mask(rem->pre_data_bits)) != rem->pre_data) {
645 log_warn(
646 "%s: %s: Invalid pre_data", path, rem->name);
647 rem->pre_data &= gen_mask(rem->pre_data_bits);
648 }
649 if ((rem->post_data & gen_mask(rem->post_data_bits)) != rem->post_data) {
650 log_warn("%s: %s: Invalid post_data",
651 path, rem->name);
652 rem->post_data &= gen_mask(rem->post_data_bits);
653 }
654 if (!rem->codes) {
655 log_error("%s: %s: No codes", path, rem->name);
656 return 0;
657 }
658 for (codes = rem->codes; codes->name != NULL; codes++) {
659 if ((codes->code & gen_mask(rem->bits)) != codes->code) {
660 log_warn("%s: %s: Invalid code : %s",
661 path, rem->name, codes->name);
662 codes->code &= gen_mask(rem->bits);
663 }
664 for (node = codes->next; node != NULL; node = node->next) {
665 if ((node->code & gen_mask(rem->bits)) != node->code) {
666 log_warn("%s: %s: Invalid code %s: %s",
667 path, rem->name, codes->name);
668 node->code &= gen_mask(rem->bits);
669 }
670 }
671 }
672 return 1;
673}
674
681static int remote_bits_cmp(struct ir_remote* r1, struct ir_remote* r2)
682{
683 int r1_size;
684 int r2_size;
685 struct ir_ncode* c;
686
687 int r1_is_raw = is_raw(r1);
688 int r2_is_raw = is_raw(r2);
689
690 if (!r1_is_raw && r2_is_raw)
691 return -1;
692 if (r1_is_raw && !r2_is_raw)
693 return 1;
694
695 if (r1_is_raw && r2_is_raw) {
696 for (c = r1->codes, r1_size = 0; c->name != NULL; c++)
697 r1_size += 1;
698 for (c = r2->codes, r2_size = 0; c->name != NULL; c++)
699 r2_size += 1;
700 } else {
701 r1_size = bit_count(r1);
702 r2_size = bit_count(r2);
703 }
704 if (r1_size == r2_size)
705 return 0;
706 return r1_size < r2_size ? -1 : 1;
707}
708
709
714static struct ir_remote* sort_by_bit_count(struct ir_remote* remotes)
715{
716 struct ir_remote* top;
717 struct ir_remote* rem;
718 struct ir_remote* next;
719 struct ir_remote* prev;
720 struct ir_remote* scan;
721 struct ir_remote* r;
722
723 for (r = remotes; r != NULL && r != (void*)-1; r = r->next)
724 if (r->manual_sort)
725 return remotes;
726 rem = remotes;
727 top = NULL;
728 while (rem != NULL && rem != (void*)-1) {
729 next = rem->next;
730
731 scan = top;
732 prev = NULL;
733 while (scan && remote_bits_cmp(scan, rem) <= 0) {
734 prev = scan;
735 scan = scan->next;
736 }
737 if (prev)
738 prev->next = rem;
739 else
740 top = rem;
741 if (scan)
742 rem->next = scan;
743 else
744 rem->next = NULL;
745
746 rem = next;
747 }
748
749 return top;
750}
751
752static const char* lirc_parse_include(char* s)
753{
754 char* last;
755 size_t len;
756
757 len = strlen(s);
758 if (len < 2)
759 return NULL;
760 last = s + len - 1;
761 while (last > s && strchr(whitespace, *last) != NULL)
762 last--;
763 if (last <= s)
764 return NULL;
765 if (*s != '"' && *s != '<')
766 return NULL;
767 if (*s == '"' && *last != '"')
768 return NULL;
769 else if (*s == '<' && *last != '>')
770 return NULL;
771 *last = 0;
772 memmove(s, s + 1, len - 2 + 1); /* terminating 0 is copied, and
773 * maybe more, but we don't care */
774 return s;
775}
776
777
779static const char* lirc_parse_relative(char* dst,
780 size_t dst_size,
781 const char* child,
782 const char* current)
783{
784 char* dir;
785 size_t dirlen;
786
787 if (!current)
788 return child;
789
790 /* Already an absolute path */
791 if (*child == '/') {
792 snprintf(dst, dst_size, "%s", child);
793 return dst;
794 }
795 if (strlen(current) >= dst_size)
796 return NULL;
797 strcpy(dst, current);
798 dir = dirname(dst);
799 dirlen = strlen(dir);
800 if (dir != dst)
801 memmove(dst, dir, dirlen + 1);
802
803 if (dirlen + 1 + strlen(child) + 1 > dst_size)
804 return NULL;
805 strcat(dst, "/");
806 strcat(dst, child);
807
808 return dst;
809}
810
811
813static struct ir_remote*
814ir_remotes_append(struct ir_remote* root, struct ir_remote* what)
815{
816 struct ir_remote* r;
817
818 if (root == (struct ir_remote*)-1)
819 root = NULL;
820 if (what == (struct ir_remote*)-1)
821 what = NULL;
822 if (root == NULL && what != NULL)
823 return what;
824 if (what == NULL)
825 return root;
826 for (r = root; r->next != NULL; r = r->next)
827 ;
828 r->next = what;
829 return root;
830}
831
832
833struct ir_remote* read_config(FILE* f, const char* name)
834{
835 struct ir_remote* head;
836
837 head = read_config_recursive(f, name, 0);
838 head = sort_by_bit_count(head);
839 return head;
840}
841
842
853static struct ir_remote*
854read_included(const char* name, int depth, char* val, struct ir_remote* top_rem)
855{
856 FILE* childFile;
857 const char* childName;
858 struct ir_remote* rem = NULL;
859
860 if (depth > MAX_INCLUDES) {
861 log_error("error opening child file defined at %s:%d", name, line);
862 log_error("too many files included");
863 return top_rem;
864 }
865 childName = lirc_parse_include(val);
866 if (!childName) {
867 log_error("error parsing child file value defined at line %d:", line);
868 log_error("invalid quoting");
869 return top_rem;
870 }
871 childFile = fopen(childName, "r");
872 if (childFile == NULL) {
873 log_error("error opening child file '%s' defined at line %d:",
874 childName, line);
875 log_error("ignoring this child file for now.");
876 return NULL;
877 }
878 rem = read_config_recursive(childFile, childName, depth + 1);
879 top_rem = ir_remotes_append(top_rem, rem);
880 fclose(childFile);
881 return top_rem;
882}
883
884
895static struct ir_remote* read_all_included(const char* name,
896 int depth,
897 char* val,
898 struct ir_remote* top_rem)
899{
900 int i;
901 glob_t globbuf;
902 char buff[256] = { '\0' };
903
904 memset(&globbuf, 0, sizeof(globbuf));
905 val = val + 1; // Strip quotes
906 val[strlen(val) - 1] = '\0';
907 lirc_parse_relative(buff, sizeof(buff), val, name);
908 glob(buff, 0, NULL, &globbuf);
909 for (i = 0; i < globbuf.gl_pathc; i += 1) {
910 snprintf(buff, sizeof(buff), "\"%s\"", globbuf.gl_pathv[i]);
911 top_rem = read_included(name, depth, buff, top_rem);
912 }
913 globfree(&globbuf);
914 return top_rem;
915}
916
917
918static void check_ncode_dups(const char* path,
919 const char* name,
920 struct void_array* ar,
921 struct ir_ncode* code)
922{
923 if (foreach_void_array(ar, array_guest_ncode_cmp, code) != NULL) {
924 log_notice("%s: %s: Multiple definitions of: %s",
925 path, name, code->name);
926 }
927 if (foreach_void_array(ar, array_guest_code_equals, code) != NULL) {
928 log_notice("%s: %s: Multiple values for same code: %s",
929 path, name, code->name);
930 }
931}
932
933
934static struct ir_remote*
935read_config_recursive(FILE* f, const char* name, int depth)
936{
937 char buf[LINE_LEN + 1];
938 char* key;
939 char* val;
940 char* val2;
941 int len, argc;
942 struct ir_remote* top_rem = NULL;
943 struct ir_remote* rem = NULL;
944 struct void_array codes_list, raw_codes, signals;
945 struct ir_ncode raw_code = { NULL, 0, 0, NULL };
946 struct ir_ncode name_code = { NULL, 0, 0, NULL };
947 struct ir_ncode* code;
948 int mode = ID_none;
949
950 line = 0;
951 parse_error = 0;
952 log_trace1("parsing '%s'", name);
953
954 while (fgets(buf, LINE_LEN, f) != NULL) {
955 line++;
956 len = strlen(buf);
957 if (len == LINE_LEN && buf[len - 1] != '\n') {
958 log_error("line %d too long in config file", line);
959 parse_error = 1;
960 break;
961 }
962
963 if (len > 0) {
964 len--;
965 if (buf[len] == '\n')
966 buf[len] = 0;
967 }
968 if (len > 0) {
969 len--;
970 if (buf[len] == '\r')
971 buf[len] = 0;
972 }
973 /* ignore comments */
974 if (buf[0] == '#')
975 continue;
976 key = strtok(buf, whitespace);
977 /* ignore empty lines */
978 if (key == NULL)
979 continue;
980 val = strtok(NULL, whitespace);
981 if (val != NULL) {
982 val2 = strtok(NULL, whitespace);
983 log_trace2("Tokens: \"%s\" \"%s\" \"%s\"", key, val, (val2 == NULL ? "(null)" : val));
984 if (strcasecmp("include", key) == 0) {
985 int save_line = line;
986
987 top_rem = read_all_included(name,
988 depth,
989 val,
990 top_rem);
991 line = save_line;
992 } else if (strcasecmp("begin", key) == 0) {
993 if (strcasecmp("codes", val) == 0) {
994 /* init codes mode */
995 log_trace1(" begin codes");
996 if (!checkMode(mode, ID_remote, "begin codes"))
997 break;
998 if (rem->codes) {
999 log_error("error in configfile line %d:", line);
1000 log_error("codes are already defined");
1001 parse_error = 1;
1002 break;
1003 }
1004
1005 init_void_array(&codes_list, 30, sizeof(struct ir_ncode));
1006 mode = ID_codes;
1007 } else if (strcasecmp("raw_codes", val) == 0) {
1008 /* init raw_codes mode */
1009 log_trace1(" begin raw_codes");
1010 if (!checkMode(mode, ID_remote, "begin raw_codes"))
1011 break;
1012 if (rem->codes) {
1013 log_error("error in configfile line %d:", line);
1014 log_error("codes are already defined");
1015 parse_error = 1;
1016 break;
1017 }
1018 set_protocol(rem, RAW_CODES);
1019 raw_code.code = 0;
1020 init_void_array(&raw_codes, 30, sizeof(struct ir_ncode));
1021 mode = ID_raw_codes;
1022 } else if (strcasecmp("remote", val) == 0) {
1023 /* create new remote */
1024 log_trace("parsing remote");
1025 if (!checkMode(mode, ID_none, "begin remote"))
1026 break;
1027 mode = ID_remote;
1028 if (!top_rem) {
1029 /* create first remote */
1030 log_trace1("creating first remote");
1031 rem = top_rem = s_malloc(sizeof(struct ir_remote));
1032 rem->freq = DEFAULT_FREQ;
1033 } else {
1034 /* create new remote */
1035 log_trace1("creating next remote");
1036 rem = s_malloc(sizeof(struct ir_remote));
1037 rem->freq = DEFAULT_FREQ;
1038 ir_remotes_append(top_rem, rem);
1039 }
1040 } else if (mode == ID_codes) {
1041 code = defineCode(key, val, &name_code);
1042 while (!parse_error && val2 != NULL) {
1043 if (val2[0] == '#')
1044 break; /* comment */
1045 defineNode(code, val2);
1046 val2 = strtok(NULL, whitespace);
1047 }
1048 code->current = NULL;
1049 check_ncode_dups(name, rem->name, &codes_list, code);
1050 add_void_array(&codes_list, code);
1051 } else {
1052 log_error("error in configfile line %d:", line);
1053 log_error("unknown section \"%s\"", val);
1054 parse_error = 1;
1055 }
1056 if (!parse_error && val2 != NULL) {
1057 log_warn("%s: garbage after '%s' token "
1058 "in line %d ignored",
1059 rem->name, val, line);
1060 }
1061 } else if (strcasecmp("end", key) == 0) {
1062 if (strcasecmp("codes", val) == 0) {
1063 /* end Codes mode */
1064 log_trace1(" end codes");
1065 if (!checkMode(mode, ID_codes, "end codes"))
1066 break;
1067 rem->codes = get_void_array(&codes_list);
1068 mode = ID_remote; /* switch back */
1069 } else if (strcasecmp("raw_codes", val) == 0) {
1070 /* end raw codes mode */
1071 log_trace1(" end raw_codes");
1072
1073 if (mode == ID_raw_name) {
1074 raw_code.signals = get_void_array(&signals);
1075 raw_code.length = signals.nr_items;
1076 if (raw_code.length % 2 == 0) {
1077 log_error("error in configfile line %d:", line);
1078 log_error("bad signal length");
1079 parse_error = 1;
1080 }
1081 if (!add_void_array(&raw_codes, &raw_code))
1082 break;
1083 mode = ID_raw_codes;
1084 }
1085 if (!checkMode(mode, ID_raw_codes, "end raw_codes"))
1086 break;
1087 rem->codes = get_void_array(&raw_codes);
1088 mode = ID_remote; /* switch back */
1089 } else if (strcasecmp("remote", val) == 0) {
1090 /* end remote mode */
1091 log_trace1("end remote");
1092 /* print_remote(rem); */
1093 if (!checkMode(mode, ID_remote, "end remote"))
1094 break;
1095 if (!sanityChecks(rem, name)) {
1096 parse_error = 1;
1097 break;
1098 }
1099 if (options_getboolean("lircd:dynamic-codes")) {
1100 if (rem->dyncodes_name == NULL)
1101 rem->dyncodes_name = s_strdup("unknown");
1102 rem->dyncodes[0].name = rem->dyncodes_name;
1103 rem->dyncodes[1].name = rem->dyncodes_name;
1104 }
1105 /* not really necessary because we
1106 * clear the alloced memory */
1107 rem->next = NULL;
1108 rem->last_code = NULL;
1109 mode = ID_none; /* switch back */
1110 } else if (mode == ID_codes) {
1111 code = defineCode(key, val, &name_code);
1112 while (!parse_error && val2 != NULL) {
1113 if (val2[0] == '#')
1114 break; /* comment */
1115 defineNode(code, val2);
1116 val2 = strtok(NULL, whitespace);
1117 }
1118 code->current = NULL;
1119 add_void_array(&codes_list, code);
1120 } else {
1121 log_error("error in configfile line %d:", line);
1122 log_error("unknown section %s", val);
1123 parse_error = 1;
1124 }
1125 if (!parse_error && val2 != NULL) {
1126 log_warn(
1127 "%s: garbage after '%s'"
1128 " token in line %d ignored",
1129 rem->name, val, line);
1130 }
1131 } else {
1132 switch (mode) {
1133 case ID_remote:
1134 argc = defineRemote(key, val, val2, rem);
1135 if (!parse_error
1136 && ((argc == 1 && val2 != NULL)
1137 || (argc == 2 && val2 != NULL && strtok(NULL, whitespace) != NULL))) {
1138 log_warn("%s: garbage after '%s'"
1139 " token in line %d ignored",
1140 rem->name, key, line);
1141 }
1142 break;
1143 case ID_codes:
1144 code = defineCode(key, val, &name_code);
1145 while (!parse_error && val2 != NULL) {
1146 if (val2[0] == '#')
1147 break; /* comment */
1148 defineNode(code, val2);
1149 val2 = strtok(NULL, whitespace);
1150 }
1151 code->current = NULL;
1152 check_ncode_dups(name,
1153 rem->name,
1154 &codes_list,
1155 code);
1156 add_void_array(&codes_list, code);
1157 break;
1158 case ID_raw_codes:
1159 case ID_raw_name:
1160 if (strcasecmp("name", key) == 0) {
1161 log_trace2("Button: \"%s\"", val);
1162 if (mode == ID_raw_name) {
1163 raw_code.signals = get_void_array(&signals);
1164 raw_code.length = signals.nr_items;
1165 if (raw_code.length % 2 == 0) {
1166 log_error("error in configfile line %d:",
1167 line);
1168 log_error("bad signal length");
1169 parse_error = 1;
1170 }
1171 if (!add_void_array(&raw_codes, &raw_code))
1172 break;
1173 }
1174 raw_code.name = s_strdup(val);
1175 if (!raw_code.name)
1176 break;
1177 raw_code.code++;
1178 init_void_array(&signals, 50, sizeof(lirc_t));
1179 mode = ID_raw_name;
1180 if (!parse_error && val2 != NULL) {
1181 log_warn("%s: garbage after '%s'"
1182 " token in line %d ignored",
1183 rem->name, key, line);
1184 }
1185 } else {
1186 if (mode == ID_raw_codes) {
1187 log_error("no name for signal defined at line %d",
1188 line);
1189 parse_error = 1;
1190 break;
1191 }
1192 if (!addSignal(&signals, key))
1193 break;
1194 if (!addSignal(&signals, val))
1195 break;
1196 if (val2)
1197 if (!addSignal(&signals, val2))
1198 break;
1199 while ((val = strtok(NULL, whitespace)))
1200 if (!addSignal(&signals, val))
1201 break;
1202 }
1203 break;
1204 }
1205 }
1206 } else if (mode == ID_raw_name) {
1207 if (!addSignal(&signals, key))
1208 break;
1209 } else {
1210 log_error("error in configfile line %d", line);
1211 parse_error = 1;
1212 break;
1213 }
1214 if (parse_error)
1215 break;
1216 }
1217 if (mode != ID_none) {
1218 switch (mode) {
1219 case ID_raw_name:
1220 if (raw_code.name != NULL) {
1221 free(raw_code.name);
1222 if (get_void_array(&signals) != NULL)
1223 free(get_void_array(&signals));
1224 }
1225 case ID_raw_codes:
1226 rem->codes = get_void_array(&raw_codes);
1227 break;
1228 case ID_codes:
1229 rem->codes = get_void_array(&codes_list);
1230 break;
1231 }
1232 if (!parse_error) {
1233 log_error("unexpected end of file");
1234 parse_error = 1;
1235 }
1236 }
1237 if (parse_error) {
1238 static int print_error = 1;
1239
1240 if (print_error) {
1241 log_error("reading of file '%s' failed", name);
1242 print_error = 0;
1243 }
1244 free_config(top_rem);
1245 if (depth == 0)
1246 print_error = 1;
1247 return (void*)-1;
1248 }
1249 /* kick reverse flag */
1250 /* handle RC6 flag to be backwards compatible: previous RC-6
1251 * config files did not set rc6_mask */
1252 rem = top_rem;
1253 while (rem != NULL) {
1254 if ((!is_raw(rem)) && rem->flags & REVERSE) {
1255 struct ir_ncode* codes;
1256
1257 if (has_pre(rem))
1258 rem->pre_data = reverse(rem->pre_data, rem->pre_data_bits);
1259 if (has_post(rem))
1260 rem->post_data = reverse(rem->post_data, rem->post_data_bits);
1261 codes = rem->codes;
1262 while (codes->name != NULL) {
1263 codes->code = reverse(codes->code, rem->bits);
1264 codes++;
1265 }
1266 rem->flags = rem->flags & (~REVERSE);
1267 rem->flags = rem->flags | COMPAT_REVERSE;
1268 /* don't delete the flag because we still need
1269 * it to remain compatible with older versions
1270 */
1271 }
1272 if (rem->flags & RC6 && rem->rc6_mask == 0 && rem->toggle_bit > 0) {
1273 int all_bits = bit_count(rem);
1274
1275 rem->rc6_mask = ((ir_code)1) << (all_bits - rem->toggle_bit);
1276 }
1277 if (rem->toggle_bit > 0) {
1278 int all_bits = bit_count(rem);
1279
1280 if (has_toggle_bit_mask(rem)) {
1281 log_warn("%s uses both toggle_bit and toggle_bit_mask", rem->name);
1282 } else {
1283 rem->toggle_bit_mask = ((ir_code)1) << (all_bits - rem->toggle_bit);
1284 }
1285 rem->toggle_bit = 0;
1286 }
1287 if (has_toggle_bit_mask(rem)) {
1288 if (!is_raw(rem) && rem->codes) {
1289 rem->toggle_bit_mask_state = (rem->codes->code & rem->toggle_bit_mask);
1290 if (rem->toggle_bit_mask_state)
1291 /* start with state set to 0 for backwards compatibility */
1292 rem->toggle_bit_mask_state ^= rem->toggle_bit_mask;
1293 }
1294 }
1295 if (is_serial(rem)) {
1296 lirc_t base;
1297
1298 if (rem->baud > 0) {
1299 base = 1000000 / rem->baud;
1300 if (rem->pzero == 0 && rem->szero == 0)
1301 rem->pzero = base;
1302 if (rem->pone == 0 && rem->sone == 0)
1303 rem->sone = base;
1304 }
1305 if (rem->bits_in_byte == 0)
1306 rem->bits_in_byte = 8;
1307 }
1308 if (rem->min_code_repeat > 0) {
1309 if (!has_repeat(rem) || rem->min_code_repeat > rem->min_repeat) {
1310 log_warn("invalid min_code_repeat value");
1311 rem->min_code_repeat = 0;
1312 }
1313 }
1314 calculate_signal_lengths(rem);
1315 rem = rem->next;
1316 }
1317
1318 return top_rem;
1319}
1320
1321void calculate_signal_lengths(struct ir_remote* remote)
1322{
1323 if (is_const(remote)) {
1324 remote->min_total_signal_length = min_gap(remote);
1325 remote->max_total_signal_length = max_gap(remote);
1326 } else {
1327 remote->min_gap_length = min_gap(remote);
1328 remote->max_gap_length = max_gap(remote);
1329 }
1330
1331 lirc_t min_signal_length = 0, max_signal_length = 0;
1332 lirc_t max_pulse = 0, max_space = 0;
1333 int first_sum = 1;
1334 struct ir_ncode* c = remote->codes;
1335 int i;
1336
1337 while (c->name) {
1338 struct ir_ncode code = *c;
1339 struct ir_code_node* next = code.next;
1340 int first = 1;
1341 int repeat = 0;
1342
1343 do {
1344 if (first) {
1345 first = 0;
1346 } else {
1347 code.code = next->code;
1348 next = next->next;
1349 }
1350 for (repeat = 0; repeat < 2; repeat++) {
1351 if (init_sim(remote, &code, repeat)) {
1352 lirc_t sum = send_buffer_sum();
1353
1354 if (sum) {
1355 if (first_sum || sum < min_signal_length)
1356 min_signal_length = sum;
1357 if (first_sum || sum > max_signal_length)
1358 max_signal_length = sum;
1359 first_sum = 0;
1360 }
1361 for (i = 0; i < send_buffer_length(); i++) {
1362 if (i & 1) { /* space */
1363 if (send_buffer_data()[i] > max_space)
1364 max_space = send_buffer_data()[i];
1365 } else { /* pulse */
1366 if (send_buffer_data()[i] > max_pulse)
1367 max_pulse = send_buffer_data()[i];
1368 }
1369 }
1370 }
1371 }
1372 } while (next);
1373 c++;
1374 }
1375 if (first_sum) {
1376 /* no timing data, so assume gap is the actual total
1377 * length */
1378 remote->min_total_signal_length = min_gap(remote);
1379 remote->max_total_signal_length = max_gap(remote);
1380 remote->min_gap_length = min_gap(remote);
1381 remote->max_gap_length = max_gap(remote);
1382 } else if (is_const(remote)) {
1383 if (remote->min_total_signal_length > max_signal_length) {
1384 remote->min_gap_length = remote->min_total_signal_length - max_signal_length;
1385 } else {
1386 log_warn("min_gap_length is 0 for '%s' remote",
1387 remote->name);
1388 remote->min_gap_length = 0;
1389 }
1390 if (remote->max_total_signal_length > min_signal_length) {
1391 remote->max_gap_length = remote->max_total_signal_length - min_signal_length;
1392 } else {
1393 log_warn("max_gap_length is 0 for '%s' remote", remote->name);
1394 remote->max_gap_length = 0;
1395 }
1396 } else {
1397 remote->min_total_signal_length = min_signal_length + remote->min_gap_length;
1398 remote->max_total_signal_length = max_signal_length + remote->max_gap_length;
1399 }
1400 log_trace("lengths: %lu %lu %lu %lu", remote->min_total_signal_length, remote->max_total_signal_length,
1401 remote->min_gap_length, remote->max_gap_length);
1402}
1403
1404void free_config(struct ir_remote* remotes)
1405{
1406 struct ir_remote* next;
1407 struct ir_ncode* codes;
1408
1409 while (remotes != NULL) {
1410 next = remotes->next;
1411
1412 if (remotes->dyncodes_name != NULL)
1413 free(remotes->dyncodes_name);
1414 if (remotes->name != NULL)
1415 free((void*)(remotes->name));
1416 if (remotes->codes != NULL) {
1417 codes = remotes->codes;
1418 while (codes->name != NULL) {
1419 struct ir_code_node* node;
1420 struct ir_code_node* next_node;
1421
1422 free(codes->name);
1423 if (codes->signals != NULL)
1424 free(codes->signals);
1425 node = codes->next;
1426 while (node) {
1427 next_node = node->next;
1428 free(node);
1429 node = next_node;
1430 }
1431 codes++;
1432 }
1433 free(remotes->codes);
1434 }
1435 free(remotes);
1436 remotes = next;
1437 }
1438}
void * get_void_array(struct void_array *ar)
Return the array dataptr, an array[nr_items] of item_size elements.
void *(* array_guest_func)(void *item, void *arg)
foreach_void_array argument.
Definition config_file.c:67
int add_void_array(struct void_array *ar, void *dataptr)
Add *dataptr to end of ar, re-allocating as necessary.
const struct flaglist all_flags[]
All flags i config file: Their name and mask.
Definition config_file.c:95
lirc_t send_buffer_sum(void)
Definition transmit.c:380
const lirc_t * send_buffer_data(void)
Definition transmit.c:375
int send_buffer_length(void)
Do not document this function.
Definition transmit.c:369
struct ir_remote * read_config(FILE *f, const char *name)
Parse a lircd.conf config file.
void free_config(struct ir_remote *remotes)
Free() an ir_remote instance obtained using read_config().
#define SHIFT_ENC
IR data is shift encoded (name obsolete)
#define RAW_CODES
for internal use only
#define XMP
XMP protocol.
#define RC6
IR data follows RC6 protocol.
#define REPEAT_HEADER
header is also sent before repeat code
#define GRUNDIG
encoding found on Grundig remote
#define BO
encoding found on Bang & Olufsen remote
uint64_t ir_code
Denotes an internal coded representation for an IR transmission.
#define COMPAT_REVERSE
compatibility mode for REVERSE flag
#define SPACE_FIRST
bits are encoded as space+pulse
#define SPACE_ENC
IR data is space encoded.
#define RC5
IR data follows RC5 protocol.
#define NO_FOOT_REP
no foot for key repeats
#define SERIAL
serial protocol
#define NO_HEAD_REP
no header for key repeats
#define CONST_LENGTH
signal length+gap is always constant
#define RCMM
IR data follows RC-MM protocol.
#define log_trace(fmt,...)
Log a trace message.
Definition lirc_log.h:129
#define log_notice(fmt,...)
Log a notice message.
Definition lirc_log.h:119
#define log_info(fmt,...)
Log an info message.
Definition lirc_log.h:114
#define log_trace2(fmt,...)
Log a trace2 message.
Definition lirc_log.h:139
#define log_error(fmt,...)
Log an error message.
Definition lirc_log.h:104
#define log_trace1(fmt,...)
Log a trace1 message.
Definition lirc_log.h:134
logchannel_t
Log channels used to filter messages.
Definition lirc_log.h:53
#define log_warn(fmt,...)
Log a warning message.
Definition lirc_log.h:109
Description of flag to print.
char * name
Name of flag.
int flag
Flag bitmask.
An ir_code for entering into (singly) linked lists, i.e.
IR Command, corresponding to one (command defining) line of the configuration file.
struct ir_code_node * next
Linked list of the subsequent ir_code's, after the first one.
ir_code code
The first code of the command.
int length
(private)
lirc_t * signals
(private)
char * name
Name of command.
One remote as represented in the configuration file.
const char * driver
Name of driver for LIRCCODE cases.
uint32_t repeat_gap
time between two repeat codes if different from gap
lirc_t stwo
2 (only used for RC-MM)
unsigned int freq
modulation frequency
int suppress_repeat
suppress unwanted repeats
unsigned int aeps
detecting very short pulses is difficult with relative tolerance for some remotes,...
uint32_t gap2
time between signals in usecs
lirc_t min_total_signal_length
how long is the shortest signal including gap
unsigned int stop_bits
mapping: 1->2 1.5->3 2->4
unsigned int bits_in_byte
default: 8
struct ir_ncode dyncodes[2]
helper structs for unknown buttons
lirc_t sfoot
foot
ir_code rc6_mask
RC-6 doubles signal length of some bits.
lirc_t ptrail
trailing pulse
unsigned int duty_cycle
0<duty cycle<=100 default: 50
lirc_t min_gap_length
how long is the shortest gap
ir_code repeat_mask
mask defines which bits are inverted for repeats
lirc_t srepeat
indicate repeating
ir_code pre_data
data which the remote sends before actual keycode
int bits
bits (length of code)
int post_data_bits
length of post_data
ir_code ignore_mask
mask defines which bits can be ignored when matching a code
lirc_t plead
leading pulse
lirc_t sthree
3 (only used for RC-MM)
lirc_t shead
header
ir_code post_data
data which the remote sends after actual keycode
ir_code toggle_mask
Sharp (?) error detection scheme.
int flags
flags
unsigned int baud
can be overridden by [p|s]zero, [p|s]one
int min_repeat
code is repeated at least x times code sent once -> min_repeat=0
int manual_sort
If set in any remote, disables automatic sorting.
lirc_t post_s
signal between keycode and post_code
lirc_t pre_s
signal between pre_data and keycode
uint32_t gap
time between signals in usecs
int eps
eps (relative tolerance)
char * dyncodes_name
name for unknown buttons
struct ir_ncode * last_code
code received or sent last
unsigned int min_code_repeat
meaningful only if remote sends a repeat code: in this case this value indicates how often the real c...
const char * name
name of remote control
ir_code toggle_bit_mask
previously only one bit called toggle_bit
unsigned int parity
currently unsupported
int pre_data_bits
length of pre_data
lirc_t max_gap_length
how long is the longest gap
lirc_t max_total_signal_length
how long is the longest signal including gap
int toggle_bit
obsolete