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