Skip to content

Commit

Permalink
Add missing options to cir config
Browse files Browse the repository at this point in the history
Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed May 11, 2024
1 parent 17ea37d commit 152d6ff
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 17 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ This tool replaces all those tools, but with major new features:
Pronto hex codes are a fairly straightforward way of encoding raw IR,
NEC, RC-5 and a few others.

[IRP](http://hifi-remote.com/wiki/index.php?title=IRP_Notation) is a
[IRP Notation](http://hifi-remote.com/wiki/index.php?title=IRP_Notation) is a
DSL language which can
express [any IR protocol](http://hifi-remote.com/wiki/index.php/DecodeIR).
We can parse IRP and compile a decoder to BPF using LLVM. So, any protocol can
Expand Down
53 changes: 41 additions & 12 deletions src/bin/cir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,26 +117,26 @@ struct DecodeOptions {
eps: Option<u32>,

/// Save the NFA
#[arg(long = "save-nfa", global = true, help_heading = "ADVANCED")]
#[arg(long = "save-nfa", global = true, help_heading = "DEBUGGING")]
save_nfa: bool,

/// Save the DFA
#[arg(long = "save-dfa", global = true, help_heading = "ADVANCED")]
#[arg(long = "save-dfa", global = true, help_heading = "DEBUGGING")]
save_dfa: bool,
}

#[derive(Args)]
struct BpfDecodeOptions {
/// Save the LLVM IR
#[arg(long = "save-llvm-ir", help_heading = "ADVANCED")]
#[arg(long = "save-llvm-ir", help_heading = "DEBUGGING")]
save_llvm_ir: bool,

/// Save the Assembly
#[arg(long = "save-asm", help_heading = "ADVANCED")]
#[arg(long = "save-asm", help_heading = "DEBUGGING")]
save_assembly: bool,

/// Save the Object
#[arg(long = "save-object", help_heading = "ADVANCED")]
#[arg(long = "save-object", help_heading = "DEBUGGING")]
save_object: bool,
}

Expand Down Expand Up @@ -229,6 +229,22 @@ struct List {
mapping: bool,
}

#[cfg(target_os = "linux")]
fn parse_scankey(arg: &str) -> Result<(u64, String), String> {
if let Some((scancode, keycode)) = arg.split_once('=') {
let scancode = if let Some(hex) = scancode.strip_prefix("0x") {
u64::from_str_radix(hex, 16)
} else {
str::parse(scancode)
}
.map_err(|e| format!("{e}"))?;

Ok((scancode, keycode.to_owned()))
} else {
Err("missing `=` separator".into())
}
}

#[cfg(target_os = "linux")]
#[derive(Args)]
struct Config {
Expand All @@ -252,10 +268,23 @@ struct Config {
#[arg(long = "period", short = 'P', name = "PERIOD")]
period: Option<u32>,

// TODO:
// --irp '{}<>()[]'
// set scancode (like ir-keytable --set-key/-k)
// set protocol (like ir-keytabke -P)
/// Load decoder based on IRP Notation
#[arg(long = "irp", short = 'i', name = "IRP")]
irp: Option<String>,

/// Protocol to enable
#[arg(
long = "protocol",
short = 'p',
value_delimiter = ',',
name = "PROTOCOL"
)]
protocol: Vec<String>,

/// Scancode to keycode mapping to add
#[arg(long = "set-key", short = 'k', value_parser = parse_scankey, value_delimiter = ',', name = "SCANKEY")]
scankey: Vec<(u64, String)>,

#[clap(flatten)]
options: DecodeOptions,

Expand Down Expand Up @@ -348,7 +377,7 @@ struct TransmitIrp {
#[arg(long = "field", short = 'f', value_delimiter = ',')]
fields: Vec<String>,

/// IRP protocol
/// IRP Notation
#[arg(name = "IRP")]
irp: String,
}
Expand Down Expand Up @@ -531,7 +560,7 @@ impl FromArgMatches for TransmitCommands {
impl Subcommand for TransmitCommands {
fn augment_subcommands(cmd: Command) -> Command {
cmd.subcommand(TransmitIrp::augment_args(
Command::new("irp").about("Encode using IRP language and transmit"),
Command::new("irp").about("Encode using IRP Notation and transmit"),
))
.subcommand(TransmitKeymap::augment_args(
Command::new("keymap").about("Transmit codes from keymap or lircd.conf file"),
Expand All @@ -546,7 +575,7 @@ impl Subcommand for TransmitCommands {
}
fn augment_subcommands_for_update(cmd: Command) -> Command {
cmd.subcommand(TransmitIrp::augment_args(
Command::new("irp").about("Encode using IRP language and transmit"),
Command::new("irp").about("Encode using IRP Notation and transmit"),
))
.subcommand(TransmitKeymap::augment_args(
Command::new("keymap").about("Transmit codes from keymap or lircd.conf file"),
Expand Down
139 changes: 135 additions & 4 deletions src/bin/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use cir::{
rcdev::{enumerate_rc_dev, Rcdev},
};
use evdev::Key;
use irp::Options;
use irp::{Irp, Options};
use log::debug;
use std::{
path::{Path, PathBuf},
Expand Down Expand Up @@ -85,9 +85,140 @@ pub fn config(config: &crate::Config) {
}
}

// TODO
// set scancode
// load irp
if !config.scankey.is_empty() {
for (scancode, keycode) in &config.scankey {
let key = match Key::from_str(keycode) {
Ok(key) => key,
Err(_) => {
eprintln!("error: ‘{keycode}’ is not a valid keycode");
continue;
}
};

match rcdev.update_scancode(key, *scancode) {
Ok(_) => (),
Err(e) => {
eprintln!(
"error: failed to update key mapping from scancode {scancode:x?} to {key:?}: {e}"
);
std::process::exit(1);
}
}
}
}

if !config.protocol.is_empty() {
let mut res = Vec::new();

for name in &config.protocol {
if name.is_empty() {
// nothing to do
} else if name == "all" {
for pos in 0..rcdev.supported_protocols.len() {
if !res.contains(&pos) {
res.push(pos);
}
}
} else if let Some(pos) = rcdev.supported_protocols.iter().position(|e| e == name) {
if !res.contains(&pos) {
res.push(pos);
}
} else {
eprintln!("error: {}: does not support protocol {name}", rcdev.name);
std::process::exit(1);
}
}

if let Err(e) = rcdev.set_enabled_protocols(&res) {
eprintln!("error: {}: {e}", rcdev.name);
std::process::exit(1);
}
}

if let Some(irp_notation) = &config.irp {
let irp = match Irp::parse(irp_notation) {
Ok(irp) => irp,
Err(e) => {
eprintln!("error: {irp_notation}: {e}");
std::process::exit(1);
}
};

let mut max_gap = 100000;

let chdev = if let Some(lircdev) = &rcdev.lircdev {
let lirc = match Lirc::open(PathBuf::from(lircdev)) {
Ok(fd) => fd,
Err(e) => {
eprintln!("error: {lircdev}: {e}");
std::process::exit(1);
}
};

if !lirc.can_receive_raw() {
eprintln!(
"error: {}: not a raw receiver, irp not supported",
rcdev.name
);
std::process::exit(1);
}

lirc
} else {
eprintln!("error: {}: no lirc device, irp not supported", rcdev.name);
std::process::exit(1);
};

if let Some(timeout) = config.timeout {
max_gap = timeout;
} else if let Ok(timeout) = chdev.get_timeout() {
let dev_max_gap = (timeout * 9) / 10;

log::trace!(
"device reports timeout of {}, using 90% of that as {} max_gap",
timeout,
dev_max_gap
);

max_gap = dev_max_gap;
}

let mut options = Options {
name: "irp",
max_gap,
..Default::default()
};

options.nfa = config.options.save_nfa;
options.dfa = config.options.save_dfa;
options.aeps = config.options.aeps.unwrap_or(100);
options.eps = config.options.eps.unwrap_or(3);

options.llvm_ir = config.bpf_options.save_llvm_ir;
options.assembly = config.bpf_options.save_assembly;
options.object = config.bpf_options.save_object;

let dfa = match irp.compile(&options) {
Ok(dfa) => dfa,
Err(e) => {
println!("error: irp: {e}");
std::process::exit(1);
}
};

let bpf = match dfa.compile_bpf(&options) {
Ok((bpf, _)) => bpf,
Err(e) => {
eprintln!("error: irp: {e}");
std::process::exit(1);
}
};

if let Err(e) = chdev.attach_bpf(&bpf) {
eprintln!("error: attach bpf: {e}",);
std::process::exit(1);
}
}
}

pub fn load(load: &crate::Load) {
Expand Down

0 comments on commit 152d6ff

Please sign in to comment.