diff --git a/TODO b/TODO index dcaa954..b67cb21 100644 --- a/TODO +++ b/TODO @@ -13,7 +13,6 @@ Necessary: Nice to have: - ir encode/send needs to read xml file - cir decode irp should read IrpProtocols.xml -- access to io_error in aya - cir transmit rawir -S sony15:12 -r 2 ?? - pcmak leading gap not decoded - encoding toggle_bit_mask not used when popcount > 1 diff --git a/src/bin/commands/config.rs b/src/bin/commands/config.rs index 12d35b9..3e950a7 100644 --- a/src/bin/commands/config.rs +++ b/src/bin/commands/config.rs @@ -156,13 +156,22 @@ pub fn config(config: &crate::Config) { }; if !lirc.can_receive_raw() { - eprintln!( - "error: {}: not a raw receiver, irp not supported", - rcdev.name - ); + eprintln!("error: {}: not a raw receiver, irp not supported", lircdev); std::process::exit(1); } + match lirc.query_bpf() { + Ok(Some(_)) => (), + Ok(None) => { + eprintln!("error: {}: no kernel BPF support, rebuild kernel with CONFIG_BPF_LIRC_MODE2", lircdev); + std::process::exit(1); + } + Err(e) => { + eprintln!("error: {}: {e}", lircdev); + std::process::exit(1); + } + } + lirc } else { eprintln!("error: {}: no lirc device, irp not supported", rcdev.name); @@ -475,6 +484,26 @@ fn load_keymap( } }; + if !chdev.can_receive_raw() { + eprintln!("error: {}: not a raw receiver, irp not supported", chdev); + std::process::exit(1); + } + + match chdev.query_bpf() { + Ok(Some(_)) => (), + Ok(None) => { + eprintln!( + "error: {}: no kernel BPF support, rebuild kernel with CONFIG_BPF_LIRC_MODE2", + chdev + ); + std::process::exit(1); + } + Err(e) => { + eprintln!("error: {}: {e}", chdev); + std::process::exit(1); + } + } + if let Err(e) = chdev.attach_bpf(&bpf) { eprintln!("error: {}: attach bpf: {e}", keymap_filename.display()); std::process::exit(1); @@ -546,6 +575,26 @@ fn load_lircd( } }; + if !chdev.can_receive_raw() { + eprintln!("error: {}: not a raw receiver, irp not supported", chdev); + std::process::exit(1); + } + + match chdev.query_bpf() { + Ok(Some(_)) => (), + Ok(None) => { + eprintln!( + "error: {}: no kernel BPF support, rebuild kernel with CONFIG_BPF_LIRC_MODE2", + chdev + ); + std::process::exit(1); + } + Err(e) => { + eprintln!("error: {}: {e}", chdev); + std::process::exit(1); + } + } + if let Err(e) = chdev.attach_bpf(&bpf) { eprintln!("error: {}: attach bpf: {e}", keymap_filename.display()); std::process::exit(1); diff --git a/src/bin/commands/list.rs b/src/bin/commands/list.rs index 5f19166..73ac070 100644 --- a/src/bin/commands/list.rs +++ b/src/bin/commands/list.rs @@ -215,9 +215,12 @@ fn print_rc_dev(list: &[rcdev::Rcdev], config: &crate::List) { if lircdev.can_receive_raw() { match lircdev.query_bpf() { - Ok(links) => { + Ok(Some(links)) => { println!("\tBPF protocols\t\t: {}", links.iter().join(" ")); } + Ok(None) => { + println!("\tBPF protocols\t\t: No kernel support") + } Err(err) => { println!("\tBPF protocols\t\t: {err}") } diff --git a/src/lirc.rs b/src/lirc.rs index 9fa255a..9217126 100644 --- a/src/lirc.rs +++ b/src/lirc.rs @@ -504,9 +504,17 @@ impl Lirc { } } - /// query bpf programs - pub fn query_bpf(&self) -> Result, ProgramError> { - let links = LircMode2::query(self.as_fd())?; + /// Query bpf programs attached to lirc dev. Returns Ok(None) if there is no support + /// for bpf programs with the current kernel. + pub fn query_bpf(&self) -> Result>, ProgramError> { + let res = LircMode2::query(self.as_fd()); + + if res.as_ref().is_err_and(is_syscall_unsupported) { + return Ok(None); + } + + let links = res?; + let mut res = Vec::new(); for link in links { @@ -517,15 +525,23 @@ impl Lirc { } } - Ok(res) + Ok(Some(res)) } /// Remove all attached bpf programs pub fn clear_bpf(&self) -> Result<(), ProgramError> { - let links = LircMode2::query(self.as_fd())?; + let res = LircMode2::query(self.as_fd()); + + if res.as_ref().is_err_and(is_syscall_unsupported) { + return Ok(()); + } + + let links = res?; + for link in links { link.detach()?; } + Ok(()) } } @@ -547,3 +563,22 @@ impl fmt::Display for Lirc { write!(f, "{}", self.path.display()) } } + +/// If LircMode2::query() fails, is this due to the syscall not being supported, +/// or another reason (e.g. permission denied) +fn is_syscall_unsupported(error: &ProgramError) -> bool { + if let ProgramError::SyscallError(syscall_error) = &error { + // No support for the syscall + if syscall_error.call == "bpf_prog_query" + // kernel not built with CONFIG_BPF_LIRC_MODE2 + && syscall_error.io_error.kind() == ErrorKind::InvalidInput + // kernel not built with CONFIG_BPF_SYSCALL + || syscall_error.io_error.kind() == ErrorKind::Unsupported + { + return true; + } + } + + // another reason (permission denied, not sure what else could go wrong) + false +}