aboutsummaryrefslogtreecommitdiffstats
path: root/utils/keytable/bpf_protocols/grundig.c
blob: 4d8cc4b92a2378f8785f885bea3e71c37ef37a44 (plain)
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
// 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 grundig_state {
	STATE_INACTIVE,
	STATE_HEADER_SPACE,
	STATE_LEADING_PULSE,
	STATE_BITS_SPACE,
	STATE_BITS_PULSE,
};

struct decoder_state {
	unsigned int bits;
	enum grundig_state state;
	unsigned int count;
	unsigned int last_space;
};

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,
};

static inline int eq_margin(unsigned d1, unsigned d2, unsigned margin)
{
	return ((d1 > (d2 - margin)) && (d1 < (d2 + margin)));
}

// 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 header_pulse = 900;
int header_space = 2900;
int leader_pulse = 1300;

#define BPF_PARAM(x) (int)(&(x))

SEC("grundig")
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);

	if (s->state == STATE_INACTIVE) {
		if (LIRC_IS_PULSE(*sample) && eq_margin(BPF_PARAM(header_pulse), duration, 100)) {
			s->bits = 0;
			s->state = STATE_HEADER_SPACE;
			s->count = 0;
		}
	} else if (s->state == STATE_HEADER_SPACE) {
		if (LIRC_IS_SPACE(*sample) && eq_margin(BPF_PARAM(header_space), duration, 200))
			s->state = STATE_LEADING_PULSE;
		else
			s->state = STATE_INACTIVE;
	} else if (s->state == STATE_LEADING_PULSE) {
		if (LIRC_IS_PULSE(*sample) && eq_margin(BPF_PARAM(leader_pulse), duration, 100))
			s->state = STATE_BITS_SPACE;
		else
			s->state = STATE_INACTIVE;
	} else if (s->state == STATE_BITS_SPACE) {
		s->last_space = duration;
		s->state = STATE_BITS_PULSE;
	} else if (s->state == STATE_BITS_PULSE) {
		int t = -1;
		if (eq_margin(s->last_space, (472), (150)) &&
		    eq_margin(duration, (583), (150)))
			t = 0;
		if (eq_margin(s->last_space, (1139), (150)) &&
		    eq_margin(duration, (583), (150)))
			t = 1;
		if (eq_margin(s->last_space, (1806), (150)) &&
		    eq_margin(duration, (583), (150)))
			t = 2;
		if (eq_margin(s->last_space, (2200), (150)) &&
		    eq_margin(duration, (1139), (150)))
			t = 3;
		if (t < 0) {
			s->state = STATE_INACTIVE;
		} else {
			s->bits <<= 2;
			switch (t) {
			case 3: s->bits |= 0; break;
			case 2: s->bits |= 3; break;
			case 1: s->bits |= 2; break;
			case 0: s->bits |= 1; break;
			}
			s->count += 2;
			if (s->count == 16) {
				bpf_rc_keydown(sample, 0x40, s->bits, 0);
				s->state = STATE_INACTIVE;
			} else {
				s->state = STATE_BITS_SPACE;
			}
		}
	}

	return 0;
}

char _license[] SEC("license") = "GPL";

Privacy Policy