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