Skip to contentSkip to conversation

keytable: add support for keymap with raw literals

PATCH New Sean Young 1 participant 1 message Aug 8, 2019 Media (V4L/DVB)

lore
Share & tools
lore

Sean Young

author
#0 lore raw

PATCH v2 1/1 keytable: add support for keymap with raw literals

These rc keymaps look like:

	[[protocols]]
	name = 'Beon_Colour_TV'
	protocol = 'raw'
	[[protocols.raw]]
	keycode = 'KEY_POWER'
	raw = [ 1324, 842, 6697, 26400, 1324, 842, 6697 ]
	[[protocols.raw]]
	keycode = 'KEY_MUTE'
	raw = [ 1305, 865, 2357, 931, 3411, 26400, 1305, 865, 2357, 931, 3411 ]

So the incoming IR will be literally compared against the IR given, and only if the entire array matches with no leading or trailing IR will the keycode be reported.

This is analogous to lircd raw_codes support, so lircd2toml.py can also convert raw_codes lircd.conf files. This means that the vast majority of lircd files can now be converted.

This feature requires loops in BPF programs so this is only supported in kernel 5.3 onwards.

---
 contrib/lircd2toml.py                    |  76 ++++++++----
 utils/keytable/bpf_load.c                | 108 +++++++++++++++-
 utils/keytable/bpf_load.h                |   9 +-
 utils/keytable/bpf_protocols/Makefile.am |   2 +-
 utils/keytable/bpf_protocols/bitmap.h    |  36 ++++++
 utils/keytable/bpf_protocols/raw.c       | 149 +++++++++++++++++++++++
 utils/keytable/keytable.c                | 139 ++++++++++++++++++++-
 utils/keytable/rc_keymap.5.in            |  18 ++-
 8 files changed, 505 insertions(+), 32 deletions(-)
 create mode 100644 utils/keytable/bpf_protocols/bitmap.h
 create mode 100644 utils/keytable/bpf_protocols/raw.c
contrib/lircd2toml.py +52 -24
index 9b93f198..5a60cfaa 100755
@@ -146,14 +146,14 @@ class LircdParser:
             a = line.split()
             if a[0] == 'name':
                 if len(codes) > 0:
-                    raw_codes.append({ 'name': name, 'codes': codes })
+                    raw_codes.append({ 'keycode': name, 'raw': codes })
                 name = line.split(maxsplit=2)[1]
                 if not name.startswith('KEY_'):
                     name = 'KEY_' + name.upper()
                 codes = []
             elif a[0] == 'end':
                 if len(codes) > 0:
-                    raw_codes.append({ 'name': name, 'codes': codes })
+                    raw_codes.append({ 'keycode': name, 'raw': codes })
                 return raw_codes
             else:
                 for v in a:
@@ -224,6 +224,8 @@ class Converter:
             return self.convert_rcmm()
         elif 'space_enc' in flags:
             return self.convert_space_enc()
+        elif 'raw_codes' in flags:
+            return self.convert_raw_codes()
         else:
             self.error('Cannot convert remote with flags: {}'.format('|'.join(flags)))
 
@@ -237,7 +239,7 @@ class Converter:
         res  = {
             'protocol': 'pulse_distance',
             'params': {},
-            'map': {}
+            'scancodes': {}
         }
 
         res['name'] = self.remote['name']
@@ -338,7 +340,7 @@ class Converter:
                 elif v != variant:
                     variant = ""
 
-                res['map'][n] = self.remote['codes'][s]
+                res['scancodes'][n] = self.remote['codes'][s]
 
             if variant:
                 res['params']['variant'] = "'" + variant + "'"
@@ -374,14 +376,14 @@ class Converter:
                 elif v != variant:
                     variant = ""
 
-                res['map'][n] = self.remote['codes'][s]
+                res['scancodes'][n] = self.remote['codes'][s]
 
             if variant:
                 res['params']['variant'] = "'" + variant + "'"
         else:
             for s in self.remote['codes']:
                 p = (s<<post_data_bits)|pre_data
-                res['map'][p] = self.remote['codes'][s]
+                res['scancodes'][p] = self.remote['codes'][s]
 
         return res
 
@@ -389,7 +391,7 @@ class Converter:
         res  = {
             'protocol': 'rc-mm',
             'params': {},
-            'map': {}
+            'scancodes': {}
         }
 
         res['name'] = self.remote['name']
@@ -415,9 +417,9 @@ class Converter:
             pre_data = int(self.remote['pre_data'][0]) << bits
             bits += pre_data_bits
             for s in self.remote['codes']:
-                res['map'][s|pre_data] = self.remote['codes'][s]
+                res['scancodes'][s|pre_data] = self.remote['codes'][s]
         else:
-            res['map'] = self.remote['codes']
+            res['scancodes'] = self.remote['codes']
 
         res['params']['bits'] = bits
         res['params']['variant'] = "'rc-mm-" + str(bits) + "'"
@@ -431,7 +433,7 @@ class Converter:
         res = {
             'protocol': 'rc-6',
             'params': { },
-            'map': { }
+            'scancodes': { }
         }
 
         res['name'] = self.remote['name']
@@ -471,7 +473,7 @@ class Converter:
             d = ~(s|pre_data)&mask
             if bits == 32:
                 vendor = d >> 16
-            res['map'][d] = self.remote['codes'][s]
+            res['scancodes'][d] = self.remote['codes'][s]
 
         if bits == 16:
             res['params']['variant'] = "'rc-6-0'"
@@ -494,7 +496,7 @@ class Converter:
         res  = {
             'protocol': 'manchester',
             'params': { },
-            'map': { }
+            'scancodes': { }
         }
 
         res['name'] = self.remote['name']
@@ -560,10 +562,20 @@ class Converter:
                 n = s|pre_data
                 n = (n & 0x3f) | ((n << 2) & 0x1f00)
                 newcodes[n] = self.remote['codes'][s]
-            res['map'] = newcodes
+            res['scancodes'] = newcodes
         else:
             for s in self.remote['codes']:
-                res['map'][s|pre_data] = self.remote['codes'][s]
+                res['scancodes'][s|pre_data] = self.remote['codes'][s]
+
+        return res
+
+    def convert_raw_codes(self):
+        res  = {
+            'protocol': 'raw',
+            'params': {},
+            'raw': self.remote['raw_codes'],
+            'name': self.remote['name']
+        }
 
         return res
 
@@ -576,16 +588,32 @@ def writeTOMLFile(fh, remote):
     print('protocol = {}'.format(escapeString(remote['protocol'])), file=fh)
     for p in remote['params']:
         print('{} = {}'.format(p, remote['params'][p]), file=fh)
-    print('[protocols.scancodes]', file=fh)
-    # find the largest scancode
-    length=1
-    for c in remote['map']:
-        length=max(length, c.bit_length())
-
-    # width seems to include '0x', hence the + 2
-    width = math.ceil(length/4) + 2
-    for c in remote['map']:
-        print('{:#0{width}x} = {}'.format(c, escapeString(remote['map'][c]), width=width), file=fh)
+
+    if 'scancodes' in remote:
+        print('[protocols.scancodes]', file=fh)
+        # find the largest scancode
+        length=1
+        for c in remote['scancodes']:
+            length=max(length, c.bit_length())
+
+        # width seems to include '0x', hence the + 2
+        width = math.ceil(length/4) + 2
+        for c in remote['scancodes']:
+            print('{:#0{width}x} = {}'.format(c, escapeString(remote['scancodes'][c]), width=width), file=fh)
+
+    elif 'raw' in remote:
+        for raw in remote['raw']:
+            print('[[protocols.raw]]', file=fh)
+            print('keycode = {}\nraw = ['.format(escapeString(raw['keycode'])), file=fh, end='')
+            first = True
+            for v in raw['raw']:
+                if first:
+                    print(' {}'.format(v), file=fh, end='')
+                else:
+                    print(', {}'.format(v), file=fh, end='')
+                first = False
+
+            print(' ]', file=fh)
 
     return True
 
utils/keytable/bpf_load.c +104 -4

156 lines not on this page · raw message

utils/keytable/bpf_load.h +8 -1
index 2775607f..471600bb 100644
@@ -24,6 +24,13 @@ struct bpf_map_data {
 	struct bpf_load_map_def def;
 };
 
+struct raw_entry {
+	struct raw_entry *next;
+	u_int32_t scancode;
+	u_int32_t raw_length;
+	u_int32_t raw[1];
+};
+
 /* parses elf file compiled by llvm .c->.o
  * . parses 'maps' section and creates maps via BPF syscall
  * . parses 'license' section and passes it to syscall
@@ -36,7 +43,7 @@ struct bpf_map_data {
  *
  * returns zero on success
  */
-int load_bpf_file(const char *path, int lirc_fd, struct toml_table_t *toml);
+int load_bpf_file(const char *path, int lirc_fd, struct toml_table_t *toml, struct raw_entry *raw);
 
 int bpf_param(const char *name, int *val);
 
utils/keytable/bpf_protocols/Makefile.am +1 -1
index bca3bdec..d9facbc9 100644
@@ -10,7 +10,7 @@ CLANG_SYS_INCLUDES := $(shell $(CLANG) -v -E - </dev/null 2>&1 \
 %.o: %.c bpf_helpers.h
 	$(CLANG) $(CLANG_SYS_INCLUDES) -D__linux__ -I$(top_srcdir)/include -target bpf -O2 -c $<
 
-PROTOCOLS = grundig.o pulse_distance.o pulse_length.o rc_mm.o manchester.o xbox-dvd.o imon_rsc.o
+PROTOCOLS = grundig.o pulse_distance.o pulse_length.o rc_mm.o manchester.o xbox-dvd.o imon_rsc.o raw.o
 
 all: $(PROTOCOLS)
 
utils/keytable/bpf_protocols/bitmap.h new file +36 -0
new file mode 100644
index 00000000..e3d731ad
@@ -0,0 +1,36 @@
+
+#ifndef __BITMAP_H__
+#define __BITMAP_H__
+
+#include "string.h"
+
+#define BITS_PER_LONG 64
+#define BITS_TO_LONG(n) \
+	(((n) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+
+
+#define DECLARE_BITMAP(name, bits) \
+	unsigned long name[BITS_TO_LONG(bits)]
+
+static void inline bitmap_zero(unsigned long *bitmap, int bits)
+{
+	for (int i = 0; i < BITS_TO_LONG(bits); i++)
+		bitmap[i] = 0;
+}
+
+static void inline bitmap_fill(unsigned long *bitmap, int bits)
+{
+	for (int i = 0; i < BITS_TO_LONG(bits); i++)
+		bitmap[i] = ~0;
+}
+
+#define bitmap_set(b, n) \
+	b[n / BITS_PER_LONG] |= 1 << (n % BITS_PER_LONG)
+
+#define bitmap_clear(b, n) \
+	b[n / BITS_PER_LONG] &= ~(1 << (n % BITS_PER_LONG))
+
+#define bitmap_test(b, n) \
+	(b[n / BITS_PER_LONG] & (1 << (n % BITS_PER_LONG))) != 0
+
+#endif
utils/keytable/bpf_protocols/raw.c new file +149 -0

149 lines not on this page · raw message

utils/keytable/keytable.c +138 -1

175 lines not on this page · raw message

utils/keytable/rc_keymap.5.in +17 -1
index c9e672d7..d5de657a 100644
@@ -52,7 +52,8 @@ Protocol \fBrc\-mm\fR has variants \fBrc-mm-12\fR, \fBrc-mm-24\fR, and
 \fBrc-mm-32\fR.
 .SS Scancodes field
 The \fBscancodes\fR table list the scancodes and the mapping to linux input
-key events. Multiple scancodes can map to the same key event.
+key events. Multiple scancodes can map to the same key event. This field
+is not present for \fBraw\fR protocols.
 .PP
 If the scancode start with 0x, it is interpreted as a hexadecimal number. If
 it starts with a 0, it is interpreted as an octal number.
@@ -60,6 +61,16 @@ it starts with a 0, it is interpreted as an octal number.
 The key events are listed in the \fBinput-event-codes.h\fR header file.
 Examples are \fBKEY_ENTER\fR, \fBKEY_ESC\fR or \fBBTN_LEFT\fR for the left
 mouse button.
+.SS Raw field
+If the protocol is \fBraw\fR, the \fBraw\fR field is an array of keycode
+to raw mapping. For each entry, there is a \fBkeycode\fR field and \fBraw\fR
+field. The \fBkeycode\fR is a linux input event, as explained the scancodes
+section.
+.PP
+The \fBraw\fR field is an array of integers. These are the pulse and space
+values of the IR message. The first is a pulse value in microseconds, and
+the second a space, third pulse, etc. There should be an odd number of fields
+so that the last entry is a pulse.
 .SS Remaining fields (BPF parameters)
 If the protocol is a BPF based decoder, it may have any number of numeric
 parameters. These parameters are used to support protocols with non-standard
@@ -74,6 +85,11 @@ Kernel based non-BPF protocol decoders do not have any parameters.
 Some of the BPF protocol decoders are generic and will need parameters to
 work. Other are for specific remotes and should work without any parameters.
 The timing parameters are all in microseconds (µs).
+.SS raw
+This decoder must be used when the keymap is raw; for each key, there is an
+entry in raw array with the pulse and space values for that key. No decoding
+is done, the incoming IR is simply matched against the different pulse and
+space values.
 .SS imon_rsc
 This decoder is specifically for the iMON RSC remote, which was packaged with
 the iMON Station (amongst others). The decoder is for the directional stick in
Signature · 1 line
2.21.0