mirror of
https://github.com/aya-rs/aya.git
synced 2026-03-03 18:23:55 +01:00
164 lines
5.2 KiB
Rust
164 lines
5.2 KiB
Rust
//! Lirc programs.
|
|
use std::os::fd::{AsFd, AsRawFd as _, RawFd};
|
|
|
|
use aya_obj::generated::{
|
|
bpf_attach_type::BPF_LIRC_MODE2, bpf_prog_type::BPF_PROG_TYPE_LIRC_MODE2,
|
|
};
|
|
|
|
use crate::{
|
|
programs::{
|
|
CgroupAttachMode, Link, ProgramData, ProgramError, ProgramFd, ProgramInfo, ProgramType,
|
|
id_as_key, load_program, query,
|
|
},
|
|
sys::{ProgQueryTarget, bpf_prog_attach, bpf_prog_detach, bpf_prog_get_fd_by_id},
|
|
};
|
|
|
|
/// 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)]
|
|
/// # Ebpf(#[from] aya::EbpfError)
|
|
/// # }
|
|
/// # let mut bpf = aya::Ebpf::load(&[])?;
|
|
/// use std::fs::File;
|
|
/// use aya::programs::LircMode2;
|
|
///
|
|
/// let file = File::open("/dev/lirc0")?;
|
|
/// let mut bpf = aya::Ebpf::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 {
|
|
/// The type of the program according to the kernel.
|
|
pub const PROGRAM_TYPE: ProgramType = ProgramType::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: AsFd>(&mut self, lircdev: T) -> Result<LircLinkId, ProgramError> {
|
|
let prog_fd = self.fd()?;
|
|
|
|
// The link is going to own this new file descriptor so we are
|
|
// going to need a duplicate whose lifetime we manage. Let's
|
|
// duplicate it prior to attaching it so the new file
|
|
// descriptor is closed at drop in case it fails to attach.
|
|
let prog_fd = prog_fd.try_clone()?;
|
|
let lircdev_fd = lircdev.as_fd().try_clone_to_owned()?;
|
|
let lircdev_fd = crate::MockableFd::from_fd(lircdev_fd);
|
|
|
|
bpf_prog_attach(
|
|
prog_fd.as_fd(),
|
|
lircdev_fd.as_fd(),
|
|
BPF_LIRC_MODE2,
|
|
CgroupAttachMode::Single.into(),
|
|
)?;
|
|
|
|
self.data.links.insert(LircLink::new(prog_fd, lircdev_fd))
|
|
}
|
|
|
|
/// Detaches the program.
|
|
///
|
|
/// See [`Self::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 caller takes the responsibility of managing the lifetime of the link. When the returned
|
|
/// [`LircLink`] is dropped, the link is detached.
|
|
pub fn take_link(&mut self, link_id: LircLinkId) -> Result<LircLink, ProgramError> {
|
|
self.data.links.forget(link_id)
|
|
}
|
|
|
|
/// Queries the lirc device for attached programs.
|
|
pub fn query<T: AsFd>(target_fd: T) -> Result<Vec<LircLink>, ProgramError> {
|
|
let target_fd = target_fd.as_fd();
|
|
let (_, prog_ids) = query(ProgQueryTarget::Fd(target_fd), BPF_LIRC_MODE2, 0, &mut None)?;
|
|
|
|
prog_ids
|
|
.into_iter()
|
|
.map(|prog_id| {
|
|
let prog_fd = bpf_prog_get_fd_by_id(prog_id)?;
|
|
let target_fd = target_fd.try_clone_to_owned()?;
|
|
let target_fd = crate::MockableFd::from_fd(target_fd);
|
|
let prog_fd = ProgramFd(prog_fd);
|
|
Ok(LircLink::new(prog_fd, target_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: ProgramFd,
|
|
target_fd: crate::MockableFd,
|
|
}
|
|
|
|
impl LircLink {
|
|
pub(crate) const fn new(prog_fd: ProgramFd, target_fd: crate::MockableFd) -> Self {
|
|
Self { prog_fd, target_fd }
|
|
}
|
|
|
|
/// Get [`ProgramInfo`] from this link.
|
|
pub fn info(&self) -> Result<ProgramInfo, ProgramError> {
|
|
let Self {
|
|
prog_fd,
|
|
target_fd: _,
|
|
} = self;
|
|
ProgramInfo::new_from_fd(prog_fd.as_fd())
|
|
}
|
|
}
|
|
|
|
impl Link for LircLink {
|
|
type Id = LircLinkId;
|
|
|
|
fn id(&self) -> Self::Id {
|
|
LircLinkId(self.prog_fd.as_fd().as_raw_fd(), self.target_fd.as_raw_fd())
|
|
}
|
|
|
|
fn detach(self) -> Result<(), ProgramError> {
|
|
bpf_prog_detach(self.prog_fd.as_fd(), self.target_fd.as_fd(), BPF_LIRC_MODE2)
|
|
.map_err(Into::into)
|
|
}
|
|
}
|
|
|
|
id_as_key!(LircLink, LircLinkId);
|