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 struct decoder_state {
11 unsigned int state;
12 unsigned int count;
13 unsigned long bits;
14 };
15
16 struct bpf_map_def SEC("maps") decoder_state_map = {
17 .type = BPF_MAP_TYPE_ARRAY,
18 .key_size = sizeof(unsigned int),
19 .value_size = sizeof(struct decoder_state),
20 .max_entries = 1,
21 };
22
23 // These values can be overridden in the rc_keymap toml
24 //
25 // We abuse elf relocations. We cast the address of these variables to
26 // an int, so that the compiler emits a mov immediate for the address
27 // but uses it as an int. The bpf loader replaces the relocation with the
28 // actual value (either overridden or taken from the data segment).
29 //
30 // See:
31 // http://clearwater.com.au/code/rc5
32 int margin = 200;
33 int header_pulse = 0;
34 int header_space = 0;
35 int zero_pulse = 888;
36 int zero_space = 888;
37 int one_pulse = 888;
38 int one_space = 888;
39 int toggle_bit = 100;
40 int bits = 14;
41 int scancode_mask = 0;
42 int rc_protocol = 66;
43
44 #define BPF_PARAM(x) (int)(&(x))
45
eq_margin(unsigned d1,unsigned d2)46 static inline int eq_margin(unsigned d1, unsigned d2)
47 {
48 return ((d1 > (d2 - BPF_PARAM(margin))) && (d1 < (d2 + BPF_PARAM(margin))));
49 }
50
51 #define STATE_INACTIVE 0
52 #define STATE_HEADER 1
53 #define STATE_START1 2
54 #define STATE_MID1 3
55 #define STATE_MID0 4
56 #define STATE_START0 5
57
emitBit(unsigned int * sample,struct decoder_state * s,int bit,int state)58 static int emitBit(unsigned int *sample, struct decoder_state *s, int bit, int state)
59 {
60 s->bits <<= 1;
61 s->bits |= bit;
62 s->count++;
63
64 if (s->count == BPF_PARAM(bits)) {
65 unsigned int toggle = 0;
66 unsigned long mask = BPF_PARAM(scancode_mask);
67 if (BPF_PARAM(toggle_bit) < BPF_PARAM(bits)) {
68 unsigned int tmask = 1 << BPF_PARAM(toggle_bit);
69 if (s->bits & tmask)
70 toggle = 1;
71 mask |= tmask;
72 }
73
74 bpf_rc_keydown(sample, BPF_PARAM(rc_protocol), s->bits & ~mask,
75 toggle);
76 state = STATE_INACTIVE;
77 }
78
79 return state;
80 }
81
82 SEC("manchester")
bpf_decoder(unsigned int * sample)83 int bpf_decoder(unsigned int *sample)
84 {
85 unsigned int key = 0;
86 struct decoder_state *s = bpf_map_lookup_elem(&decoder_state_map, &key);
87 if (!s)
88 return 0;
89
90 switch (*sample & LIRC_MODE2_MASK) {
91 case LIRC_MODE2_SPACE:
92 case LIRC_MODE2_PULSE:
93 case LIRC_MODE2_TIMEOUT:
94 break;
95 default:
96 // not a timing events
97 return 0;
98 }
99
100 int duration = LIRC_VALUE(*sample);
101 int pulse = LIRC_IS_PULSE(*sample);
102
103 unsigned int newState = s->state;
104
105 switch (s->state) {
106 case STATE_INACTIVE:
107 if (BPF_PARAM(header_pulse)) {
108 if (pulse &&
109 eq_margin(BPF_PARAM(header_pulse), duration)) {
110 s->state = STATE_HEADER;
111 }
112 break;
113 }
114 /* pass through */
115 case STATE_HEADER:
116 if (BPF_PARAM(header_space)) {
117 if (!pulse &&
118 eq_margin(BPF_PARAM(header_space), duration)) {
119 s->state = STATE_MID1;
120 }
121 break;
122 }
123 s->bits = 0;
124 s->count = 0;
125 /* pass through */
126 case STATE_MID1:
127 if (!pulse)
128 break;
129
130 if (eq_margin(BPF_PARAM(one_pulse), duration))
131 newState = emitBit(sample, s, 1, STATE_START1);
132 else if (eq_margin(BPF_PARAM(one_pulse) +
133 BPF_PARAM(zero_pulse), duration))
134 newState = emitBit(sample, s, 1, STATE_MID0);
135 break;
136 case STATE_MID0:
137 if (pulse)
138 break;
139
140 if (eq_margin(BPF_PARAM(zero_space), duration))
141 newState = emitBit(sample, s, 0, STATE_START0);
142 else if (eq_margin(BPF_PARAM(zero_space) +
143 BPF_PARAM(one_space), duration))
144 newState = emitBit(sample, s, 0, STATE_MID1);
145 else
146 newState = emitBit(sample, s, 0, STATE_INACTIVE);
147 break;
148 case STATE_START1:
149 if (!pulse && eq_margin(BPF_PARAM(zero_space), duration))
150 newState = STATE_MID1;
151 break;
152 case STATE_START0:
153 if (pulse && eq_margin(BPF_PARAM(one_pulse), duration))
154 newState = STATE_MID0;
155 break;
156 }
157
158 //char fmt1[] = "bits:%d state:%d newState:%d\n";
159 //bpf_trace_printk(fmt1, sizeof(fmt1), pulse, duration, s->bits);
160 //char fmt2[] = "count:%d state:%d newState:%d\n";
161 //bpf_trace_printk(fmt2, sizeof(fmt2), s->count, s->state, newState);
162
163 if (newState == s->state)
164 s->state = STATE_INACTIVE;
165 else
166 s->state = newState;
167
168 return 0;
169 }
170
171 char _license[] SEC("license") = "GPL";
172