1 // SPDX-License-Identifier: GPL-2.0+
2 //
3 // Copyright (C) 2018 Sean Young <sean@mess.org>
4
5 #include <linux/lirc.h>
6 #include <linux/bpf.h>
7
8 #include "bpf_helpers.h"
9
10 enum grundig_state {
11 STATE_INACTIVE,
12 STATE_HEADER_SPACE,
13 STATE_LEADING_PULSE,
14 STATE_BITS_SPACE,
15 STATE_BITS_PULSE,
16 };
17
18 struct decoder_state {
19 unsigned int bits;
20 enum grundig_state state;
21 unsigned int count;
22 unsigned int last_space;
23 };
24
25 struct bpf_map_def SEC("lirc_mode2/maps") decoder_state_map = {
26 .type = BPF_MAP_TYPE_ARRAY,
27 .key_size = sizeof(unsigned int),
28 .value_size = sizeof(struct decoder_state),
29 .max_entries = 1,
30 };
31
eq_margin(unsigned d1,unsigned d2,unsigned margin)32 static inline int eq_margin(unsigned d1, unsigned d2, unsigned margin)
33 {
34 return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
35 }
36
37 // These values can be overridden in the rc_keymap toml
38 //
39 // We abuse elf relocations. We cast the address of these variables to
40 // an int, so that the compiler emits a mov immediate for the address
41 // but uses it as an int. The bpf loader replaces the relocation with the
42 // actual value (either overridden or taken from the data segment).
43 int header_pulse = 900;
44 int header_space = 2900;
45 int leader_pulse = 1300;
46
47 #define BPF_PARAM(x) (int)(long)(&(x))
48
49 SEC("lirc_mode2/grundig")
bpf_decoder(unsigned int * sample)50 int bpf_decoder(unsigned int *sample)
51 {
52 unsigned int key = 0;
53 struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
54
55 if (!s)
56 return 0;
57
58 switch (*sample & LIRC_MODE2_MASK) {
59 case LIRC_MODE2_SPACE:
60 case LIRC_MODE2_PULSE:
61 case LIRC_MODE2_TIMEOUT:
62 break;
63 default:
64 // not a timing events
65 return 0;
66 }
67
68 int duration = LIRC_VALUE(*sample);
69
70 if (s->state == STATE_INACTIVE) {
71 if (LIRC_IS_PULSE(*sample) && eq_margin(BPF_PARAM(header_pulse), duration, 100)) {
72 s->bits = 0;
73 s->state = STATE_HEADER_SPACE;
74 s->count = 0;
75 }
76 } else if (s->state == STATE_HEADER_SPACE) {
77 if (LIRC_IS_SPACE(*sample) && eq_margin(BPF_PARAM(header_space), duration, 200))
78 s->state = STATE_LEADING_PULSE;
79 else
80 s->state = STATE_INACTIVE;
81 } else if (s->state == STATE_LEADING_PULSE) {
82 if (LIRC_IS_PULSE(*sample) && eq_margin(BPF_PARAM(leader_pulse), duration, 100))
83 s->state = STATE_BITS_SPACE;
84 else
85 s->state = STATE_INACTIVE;
86 } else if (s->state == STATE_BITS_SPACE) {
87 s->last_space = duration;
88 s->state = STATE_BITS_PULSE;
89 } else if (s->state == STATE_BITS_PULSE) {
90 int t = -1;
91 if (eq_margin(s->last_space, (472), (150)) &&
92 eq_margin(duration, (583), (150)))
93 t = 0;
94 if (eq_margin(s->last_space, (1139), (150)) &&
95 eq_margin(duration, (583), (150)))
96 t = 1;
97 if (eq_margin(s->last_space, (1806), (150)) &&
98 eq_margin(duration, (583), (150)))
99 t = 2;
100 if (eq_margin(s->last_space, (2200), (150)) &&
101 eq_margin(duration, (1139), (150)))
102 t = 3;
103 if (t < 0) {
104 s->state = STATE_INACTIVE;
105 } else {
106 s->bits <<= 2;
107 switch (t) {
108 case 3: s->bits |= 0; break;
109 case 2: s->bits |= 3; break;
110 case 1: s->bits |= 2; break;
111 case 0: s->bits |= 1; break;
112 }
113 s->count += 2;
114 if (s->count == 16) {
115 bpf_rc_keydown(sample, 0x40, s->bits, 0);
116 s->state = STATE_INACTIVE;
117 } else {
118 s->state = STATE_BITS_SPACE;
119 }
120 }
121 }
122
123 return 0;
124 }
125
126 char _license[] SEC("license") = "GPL";
127