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