• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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