Skip to content

Commit

Permalink
Add encoder for same bpf protocols that ir-ctl can encode
Browse files Browse the repository at this point in the history
Create IRPs for old v4l-utils bpf protocols.

Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed Apr 27, 2024
1 parent 5e43ca6 commit b284293
Show file tree
Hide file tree
Showing 7 changed files with 452 additions and 9 deletions.
22 changes: 15 additions & 7 deletions libirctl/src/bpf_encoder.c
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,24 @@ static void encode_pulse_length(struct keymap *map, uint32_t scancode, int *buf,
if (keymap_param(map, "reverse", 0)) {
for (i = 0; i < bits; i++) {
if (scancode & (1 << i))
buf[len++] = keymap_param(map, "bit_1_space", 1625);
buf[len++] = keymap_param(map, "bit_1_pulse", 1625);
else
buf[len++] = keymap_param(map, "bit_0_space", 375);
buf[len++] = keymap_param(map, "bit_0_pulse", 375);

buf[len++] = keymap_param(map, "bit_pulse", 625);
buf[len++] = keymap_param(map, "bit_space", 625);
}
} else {
for (i = bits - 1; i >= 0; i--) {
if (scancode & (1 << i))
buf[len++] = keymap_param(map, "bit_1_space", 1625);
buf[len++] = keymap_param(map, "bit_1_pulse", 1625);
else
buf[len++] = keymap_param(map, "bit_0_space", 375);
buf[len++] = keymap_param(map, "bit_0_pulse", 375);

buf[len++] = keymap_param(map, "bit_pulse", 625);
buf[len++] = keymap_param(map, "bit_space", 625);
}
}

*length = len;
*length = len - 1;
}

static void manchester_advance_space(int *buf, int *len, unsigned length)
Expand All @@ -101,6 +101,14 @@ static void encode_manchester(struct keymap *map, uint32_t scancode, int *buf, i
{
int len = 0, bits, i;

int header_pulse = keymap_param(map, "header_pulse", 0);
int header_space = keymap_param(map, "header_space", 0);

if (header_pulse > 0) {
manchester_advance_pulse(buf, &len, header_pulse);
manchester_advance_space(buf, &len, header_space);
}

bits = keymap_param(map, "bits", 14);

for (i = bits - 1; i >= 0; i--) {
Expand Down
2 changes: 1 addition & 1 deletion libirctl/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ pub struct raw_entry {
}

extern "C" {
pub fn parse_keymap(fname: *const c_char, keymap: *const *const keymap, verbose: bool) -> i32;
pub fn parse_keymap(fname: *const c_char, keymap: *mut *mut keymap, verbose: bool) -> i32;
pub fn free_keymap(keymap: *const keymap);
pub fn keymap_param(keymap: *const keymap, name: *const c_char, fallback: i32) -> i32;
}
Expand Down
249 changes: 248 additions & 1 deletion src/keymap.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Parse linux rc keymaps
use std::{collections::HashMap, ffi::OsStr, path::Path};
use std::{collections::HashMap, ffi::OsStr, fmt::Write, path::Path};
use toml::{Table, Value};

#[derive(PartialEq, Eq, Debug, Default)]
Expand All @@ -9,6 +9,7 @@ pub struct Protocol {
pub protocol: String,
pub variant: Option<String>,
pub irp: Option<String>,
pub rc_protocol: Option<u16>,
pub raw: Option<Vec<Raw>>,
pub scancodes: Option<HashMap<String, String>>,
}
Expand Down Expand Up @@ -120,6 +121,18 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Keymap, String> {
variant = Some(entry.to_owned());
}

let mut rc_protocol = None;
if let Some(Value::Integer(n)) = entry.get("rc_protocol") {
if let Ok(n) = (*n).try_into() {
rc_protocol = Some(n);
} else {
return Err(format!(
"{}: rc_protocol {n} must be 16 bit value",
filename.display()
));
}
}

let mut irp = None;
let mut raw = None;
let mut scancodes = None;
Expand Down Expand Up @@ -184,6 +197,11 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Keymap, String> {
if let Some(Value::String(entry)) = entry.get("irp") {
irp = Some(entry.to_owned());
}
} else if entry.get("irp").is_some() {
return Err("set the protocol to irp when using irp".to_string());
} else if let Some(rewrite) = bpf_protocol_irp(protocol, entry.as_table().unwrap()) {
log::info!("{}: protocol {protocol} has been rewritten to irp {rewrite}", filename.display());
irp = Some(rewrite);
}

if let Some(Value::Table(codes)) = entry.get("scancodes") {
Expand All @@ -206,6 +224,7 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Keymap, String> {
protocol: protocol.to_owned(),
variant,
raw,
rc_protocol,
scancodes,
irp,
});
Expand All @@ -214,6 +233,185 @@ fn parse_toml(contents: &str, filename: &Path) -> Result<Keymap, String> {
Ok(Keymap { protocols: res })
}

fn bpf_protocol_irp(protocol: &str, entry: &Table) -> Option<String> {
let param = |name: &str, default: i64| -> i64 {
if let Some(Value::Integer(n)) = entry.get(name) {
*n
} else {
default
}
};

match protocol {
"pulse_distance" => {
let mut irp = "{".to_owned();
let bits = param("bits", 4);

if param("reverse", 0) == 0 {
irp.push_str("msb,");
}

if entry.contains_key("carrier") {
write!(irp, "{}Hz,", param("carrier", 0)).unwrap();
}

if irp.ends_with(',') {
irp.pop();
}

write!(
irp,
"}}<{},-{}|{},-{}>({},-{},CODE:{},{},-40m",
param("bit_pulse", 625),
param("bit_0_space", 375),
param("bit_pulse", 625),
param("bit_1_space", 1625),
param("header_pulse", 2125),
param("header_space", 1875),
bits,
param("trailer_pulse", 625),
)
.unwrap();

let header_optional = param("header_optional", 0);

if header_optional > 0 {
write!(
irp,
",(CODE:{},{},-40m)*",
bits,
param("trailer_pulse", 625),
)
.unwrap();
} else {
let repeat_pulse = param("repeat_pulse", 0);
if repeat_pulse > 0 {
write!(
irp,
",({},-{},{},-40)*",
repeat_pulse,
param("repeat_space", 0),
param("trailer_pulse", 625)
)
.unwrap();
}
}

write!(irp, ") [CODE:0..{}]", gen_mask(bits)).unwrap();

Some(irp)
}
"pulse_length" => {
let mut irp = "{".to_owned();
let bits = param("bits", 4);

if param("reverse", 0) == 0 {
irp.push_str("msb,");
}

if entry.contains_key("carrier") {
write!(irp, "{}Hz,", param("carrier", 0)).unwrap();
}

if irp.ends_with(',') {
irp.pop();
}

write!(
irp,
"}}<{},-{}|{},-{}>({},-{},CODE:{},-40m",
param("bit_0_pulse", 375),
param("bit_space", 625),
param("bit_1_pulse", 1625),
param("bit_space", 625),
param("header_pulse", 2125),
param("header_space", 1875),
bits,
)
.unwrap();

let header_optional = param("header_optional", 0);

if header_optional > 0 {
write!(irp, ",(CODE:{},-40m)*", bits).unwrap();
} else {
let repeat_pulse = param("repeat_pulse", 0);
if repeat_pulse > 0 {
write!(
irp,
",({},-{},{},-40)*",
repeat_pulse,
param("repeat_space", 0),
param("trailer_pulse", 625)
)
.unwrap();
}
}

write!(irp, ") [CODE:0..{}]", gen_mask(bits)).unwrap();

Some(irp)
}
"manchester" => {
let mut irp = "{msb,".to_owned();
let bits = param("bits", 14);
let toggle_bit = param("toggle_bit", 100);

if entry.contains_key("carrier") {
write!(irp, "{}Hz,", param("carrier", 0)).unwrap();
}

if irp.ends_with(',') {
irp.pop();
}

write!(
irp,
"}}<-{},{}|{},-{}>(",
param("zero_space", 888),
param("zero_pulse", 888),
param("one_pulse", 888),
param("one_space", 888),
)
.unwrap();

let header_pulse = param("header_pulse", 0);
let header_space = param("header_space", 0);

if header_pulse > 0 && header_space > 0 {
write!(irp, "{},-{},", header_pulse, header_space).unwrap();
}

if toggle_bit >= bits {
write!(irp, "CODE:{},-40m", bits,).unwrap();
} else {
let leading = bits - toggle_bit;
if leading > 1 {
write!(irp, "CODE:{}:{},", leading - 1, toggle_bit + 1).unwrap();
}
write!(irp, "T:1,").unwrap();
if toggle_bit > 0 {
write!(irp, "CODE:{},", toggle_bit).unwrap();
}
irp.pop();
}

write!(irp, ",-40m) [CODE:0..{}]", gen_mask(bits)).unwrap();

Some(irp)
}
_ => None,
}
}

fn gen_mask(v: i64) -> u64 {
if v < 64 {
(1u64 << v) - 1
} else {
u64::MAX
}
}

#[test]
fn parse_toml_test() {
let s = r#"
Expand Down Expand Up @@ -358,3 +556,52 @@ fn parse_text_test() {
}
}
}

#[test]
fn parse_bpf_toml_test() {
let s = r#"
[[protocols]]
name = "meh"
protocol = "manchester"
toggle_bit = 12
[protocols.scancodes]
0x1e3b = "KEY_SELECT"
0x1e3d = "KEY_POWER2"
0x1e1c = "KEY_TV"
"#;

let k = parse(s, Path::new("x.toml")).unwrap();

assert_eq!(k.protocols[0].name, "meh");
assert_eq!(k.protocols[0].protocol, "manchester");
assert_eq!(
k.protocols[0].irp,
Some("{msb}<-888,888|888,-888>(CODE:1:13,T:1,CODE:12,-40m) [CODE:0..16383]".into())
);

let s = r#"
[[protocols]]
name = "meh"
protocol = "manchester"
toggle_bit = 1
carrier = 38000
header_pulse = 300
header_space = 350
[protocols.scancodes]
0x1e3b = "KEY_SELECT"
0x1e3d = "KEY_POWER2"
0x1e1c = "KEY_TV"
"#;

let k = parse(s, Path::new("x.toml")).unwrap();

assert_eq!(k.protocols[0].name, "meh");
assert_eq!(k.protocols[0].protocol, "manchester");
assert_eq!(
k.protocols[0].irp,
Some(
"{msb,38000Hz}<-888,888|888,-888>(300,-350,CODE:12:2,T:1,CODE:1,-40m) [CODE:0..16383]"
.into()
)
);
}
35 changes: 35 additions & 0 deletions testdata/rc_keymaps/RM-687C.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[[protocols]]
name = 'Sony_RM-687C'
protocol = 'pulse_length'
header_pulse = 2369
header_space = 637
bits = 12
bit_space = 637
bit_1_pulse = 1166
bit_0_pulse = 565
[protocols.scancodes]
0xa90 = 'KEY_STANDBY'
0x290 = 'KEY_MUTE'
0x5d0 = 'KEY_DISPLAY'
0x010 = 'KEY_1'
0x810 = 'KEY_2'
0x410 = 'KEY_3'
0xc10 = 'KEY_4'
0x210 = 'KEY_5'
0xa10 = 'KEY_6'
0x610 = 'KEY_7'
0xe10 = 'KEY_8'
0x110 = 'KEY_9'
0x310 = 'KEY_1-'
0x910 = 'KEY_0'
0xb10 = 'KEY_2-'
0x6d0 = 'KEY_SLEEP'
0x3f0 = 'KEY_SELECT'
0x2f0 = 'KEY_KPPLUS'
0x690 = 'KEY_NORMAL'
0xaf0 = 'KEY_KPMINUS'
0x490 = 'KEY_VOLUMEUP'
0x090 = 'KEY_CHANNELUP'
0xa50 = 'KEY_TV/VIDEO'
0xc90 = 'KEY_VOLUMEDOWN'
0x890 = 'KEY_CHANNELDOWN'
Loading

0 comments on commit b284293

Please sign in to comment.