//! 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, } 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(&mut self, lircdev: T) -> Result { 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 { self.data.links.forget(link_id) } /// Queries the lirc device for attached programs. pub fn query(target_fd: T) -> Result, 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 { 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);