You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
aya/aya/src/programs/lirc_mode2.rs

162 lines
5.2 KiB
Rust

//! Lirc programs.
use std::os::fd::{AsFd as _, AsRawFd, BorrowedFd, IntoRawFd as _, RawFd};
use crate::{
generated::{bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2},
programs::{load_program, query, Link, ProgramData, ProgramError, ProgramInfo},
sys::{bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id, SyscallError},
};
use libc::{close, dup};
/// A program used to decode IR into key events for a lirc device.
///
/// [`LircMode2`] programs can be used to inspect infrared pulses, spaces,
/// and timeouts received by a lirc IR receiver.
///
/// [lirc]: https://www.kernel.org/doc/html/latest/userspace-api/media/rc/lirc-dev.html
///
/// # Minimum kernel version
///
/// The minimum kernel version required to use this feature is 4.18.
///
/// # Examples
///
/// ```no_run
/// # #[derive(thiserror::Error, Debug)]
/// # enum Error {
/// # #[error(transparent)]
/// # IO(#[from] std::io::Error),
/// # #[error(transparent)]
/// # Map(#[from] aya::maps::MapError),
/// # #[error(transparent)]
/// # Program(#[from] aya::programs::ProgramError),
/// # #[error(transparent)]
/// # Bpf(#[from] aya::BpfError)
/// # }
/// # let mut bpf = aya::Bpf::load(&[])?;
/// use std::fs::File;
/// use aya::programs::LircMode2;
///
/// let file = File::open("/dev/lirc0")?;
/// let mut bpf = aya::Bpf::load_file("imon_rsc.o")?;
/// let decoder: &mut LircMode2 = bpf.program_mut("imon_rsc").unwrap().try_into().unwrap();
/// decoder.load()?;
/// decoder.attach(file)?;
/// # Ok::<(), Error>(())
/// ```
#[derive(Debug)]
#[doc(alias = "BPF_PROG_TYPE_LIRC_MODE2")]
pub struct LircMode2 {
pub(crate) data: ProgramData<LircLink>,
}
impl LircMode2 {
/// Loads the program inside the kernel.
pub fn load(&mut self) -> Result<(), ProgramError> {
load_program(BPF_PROG_TYPE_LIRC_MODE2, &mut self.data)
}
/// Attaches the program to the given lirc device.
///
/// The returned value can be used to detach, see [LircMode2::detach].
pub fn attach<T: AsRawFd>(&mut self, lircdev: T) -> Result<LircLinkId, ProgramError> {
let prog_fd = self.fd()?;
let prog_fd = prog_fd.as_fd();
let lircdev_fd = lircdev.as_raw_fd();
bpf_prog_attach(prog_fd, lircdev_fd, BPF_LIRC_MODE2).map_err(|(_, io_error)| {
SyscallError {
call: "bpf_prog_attach",
io_error,
}
})?;
self.data.links.insert(LircLink::new(prog_fd, lircdev_fd))
}
/// Detaches the program.
///
/// See [LircMode2::attach].
pub fn detach(&mut self, link_id: LircLinkId) -> Result<(), ProgramError> {
self.data.links.remove(link_id)
}
/// Takes ownership of the link referenced by the provided link_id.
///
/// The link will be detached on `Drop` and the caller is now responsible
/// for managing its lifetime.
pub fn take_link(&mut self, link_id: LircLinkId) -> Result<LircLink, ProgramError> {
self.data.take_link(link_id)
}
/// Queries the lirc device for attached programs.
pub fn query<T: AsRawFd>(target_fd: T) -> Result<Vec<LircLink>, ProgramError> {
let prog_ids = query(target_fd.as_raw_fd(), BPF_LIRC_MODE2, 0, &mut None)?;
let mut prog_fds = Vec::with_capacity(prog_ids.len());
for id in prog_ids {
let fd = bpf_prog_get_fd_by_id(id)?;
prog_fds.push(fd);
}
Ok(prog_fds
.into_iter()
.map(|prog_fd| {
LircLink::new(
// SAFETY: The file descriptor will stay valid because
// we are leaking it. We cannot use `OwnedFd` in here
// because LircMode2::attach also uses LircLink::new
// but with a borrowed file descriptor (of the loaded
// program) without duplicating. TODO(#612): Fix API
// or internals so this file descriptor isn't leaked
unsafe { BorrowedFd::borrow_raw(prog_fd.into_raw_fd()) },
target_fd.as_raw_fd(),
)
})
.collect())
}
}
/// The type returned by [LircMode2::attach]. Can be passed to [LircMode2::detach].
#[derive(Debug, Hash, Eq, PartialEq)]
pub struct LircLinkId(RawFd, RawFd);
#[derive(Debug)]
/// An LircMode2 Link
pub struct LircLink {
prog_fd: RawFd,
target_fd: RawFd,
}
impl LircLink {
pub(crate) fn new(prog_fd: BorrowedFd<'_>, target_fd: RawFd) -> LircLink {
LircLink {
prog_fd: prog_fd.as_raw_fd(),
target_fd: unsafe { dup(target_fd) },
}
}
/// Get ProgramInfo from this link
pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
// SAFETY: TODO(https://github.com/aya-rs/aya/issues/612): make this safe by not holding `RawFd`s.
let prog_fd = unsafe { BorrowedFd::borrow_raw(self.prog_fd) };
ProgramInfo::new_from_fd(prog_fd)
}
}
impl Link for LircLink {
type Id = LircLinkId;
fn id(&self) -> Self::Id {
LircLinkId(self.prog_fd, self.target_fd)
}
fn detach(self) -> Result<(), ProgramError> {
let _ = bpf_prog_detach(self.prog_fd, self.target_fd, BPF_LIRC_MODE2);
unsafe { close(self.target_fd) };
Ok(())
}
}