Skip to content

Commit

Permalink
On Ubuntu /sys/class/rc does not exist
Browse files Browse the repository at this point in the history
This is because the module is not loaded.

Also move enumerate() into Rcdev.

Signed-off-by: Sean Young <[email protected]>
  • Loading branch information
seanyoung committed May 18, 2024
1 parent 1bac9ce commit 692d9df
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 103 deletions.
4 changes: 2 additions & 2 deletions cir/src/bin/commands/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use cir::{
lirc::Lirc,
lircd_conf,
rc_maps::parse_rc_maps_file,
rcdev::{enumerate_rc_dev, Rcdev},
rcdev::Rcdev,
};
use evdev::KeyCode;
use irp::{Irp, Options};
Expand Down Expand Up @@ -650,7 +650,7 @@ pub enum Purpose {

/// Enumerate all rc devices and find the lirc and input devices
pub fn find_devices(device: &crate::RcDevice, purpose: Purpose) -> Rcdev {
let mut list = match enumerate_rc_dev() {
let mut list = match Rcdev::enumerate_devices() {
Ok(list) if list.is_empty() => {
eprintln!("error: no devices found");
std::process::exit(1);
Expand Down
6 changes: 3 additions & 3 deletions cir/src/bin/commands/list.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use cir::{lirc::Lirc, rcdev};
use cir::{lirc::Lirc, rcdev::Rcdev};
use evdev::Device;
use itertools::Itertools;
use std::path::PathBuf;

pub fn list(args: &crate::List) {
match rcdev::enumerate_rc_dev() {
match Rcdev::enumerate_devices() {
Ok(list) => {
print_rc_dev(&list, args);
}
Expand All @@ -15,7 +15,7 @@ pub fn list(args: &crate::List) {
}
}

fn print_rc_dev(list: &[rcdev::Rcdev], config: &crate::List) {
fn print_rc_dev(list: &[Rcdev], config: &crate::List) {
let mut printed = 0;

for rcdev in list {
Expand Down
211 changes: 113 additions & 98 deletions cir/src/rcdev.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use evdev::{Device, KeyCode};
use itertools::Itertools;
use std::{
fs::{self, OpenOptions},
io::{self, Write},
io::{self, ErrorKind, Write},
path::Path,
};

Expand Down Expand Up @@ -34,117 +34,99 @@ pub struct Rcdev {
input_chdev: Option<Device>,
}

/// Get a list of rc devices attached to the system. If none are present, not found error maybe returned
pub fn enumerate_rc_dev() -> io::Result<Vec<Rcdev>> {
let mut rcdev = Vec::new();

for entry in fs::read_dir("/sys/class/rc")? {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
let uevent = read_uevent(&path)?;
let mut lircdev = None;
let mut inputdev = None;
let mut supported_protocols = Vec::new();
let mut enabled_protocols = Vec::new();

for entry in fs::read_dir(path)? {
let entry = entry?;
if let Some(file_name) = entry.file_name().to_str() {
if file_name.starts_with("lirc") {
let uevent = read_uevent(&entry.path())?;

lircdev = Some(format!("/dev/{}", uevent.dev_name));
} else if file_name.starts_with("input") {
for entry in fs::read_dir(entry.path())? {
let entry = entry?;
if let Some(file_name) = entry.file_name().to_str() {
if file_name.starts_with("event") {
let uevent = read_uevent(&entry.path())?;

inputdev = Some(format!("/dev/{}", uevent.dev_name));
impl KeymapTable {
pub fn matches(&self, rcdev: &Rcdev) -> bool {
(self.driver == "*" || self.driver == rcdev.driver)
&& (self.table == "*" || self.table == rcdev.default_keymap)
}
}

impl Rcdev {
/// Get a list of rc devices attached to the system
pub fn enumerate_devices() -> io::Result<Vec<Rcdev>> {
let mut rcdev = Vec::new();

let entries = match fs::read_dir("/sys/class/rc") {
Ok(res) => res,
Err(e) => {
return if e.kind() == ErrorKind::NotFound {
// If /sys/class/rc doesn't exist, then the kernel was not compiled with CONFIG_RC_CORE
// or the module was not loaded
Ok(Vec::new())
} else {
Err(e)
};
}
};

for entry in entries {
let entry = entry?;
let path = entry.path();
if path.is_dir() {
let uevent = read_uevent(&path)?;
let mut lircdev = None;
let mut inputdev = None;
let mut supported_protocols = Vec::new();
let mut enabled_protocols = Vec::new();

for entry in fs::read_dir(path)? {
let entry = entry?;
if let Some(file_name) = entry.file_name().to_str() {
if file_name.starts_with("lirc") {
let uevent = read_uevent(&entry.path())?;

lircdev = Some(format!("/dev/{}", uevent.dev_name));
} else if file_name.starts_with("input") {
for entry in fs::read_dir(entry.path())? {
let entry = entry?;
if let Some(file_name) = entry.file_name().to_str() {
if file_name.starts_with("event") {
let uevent = read_uevent(&entry.path())?;

inputdev = Some(format!("/dev/{}", uevent.dev_name));
}
}
}
}
} else if file_name == "protocols" {
for protocol in fs::read_to_string(entry.path())?.split_whitespace() {
if protocol.starts_with('[') && protocol.ends_with(']') {
let protocol = &protocol[1..protocol.len() - 1];
if protocol == "lirc" {
// The kernel always outputs this entry for compatibility
continue;
} else if file_name == "protocols" {
for protocol in fs::read_to_string(entry.path())?.split_whitespace() {
if protocol.starts_with('[') && protocol.ends_with(']') {
let protocol = &protocol[1..protocol.len() - 1];
if protocol == "lirc" {
// The kernel always outputs this entry for compatibility
continue;
}
enabled_protocols.push(supported_protocols.len());
supported_protocols.push(protocol.to_owned());
} else {
supported_protocols.push(protocol.to_owned());
}
enabled_protocols.push(supported_protocols.len());
supported_protocols.push(protocol.to_owned());
} else {
supported_protocols.push(protocol.to_owned());
}
}
}
}
}

rcdev.push(Rcdev {
name: entry.file_name().to_str().unwrap().to_owned(),
device_name: uevent.dev_name,
driver: uevent.drv_name,
default_keymap: uevent.name,
inputdev,
lircdev,
enabled_protocols,
supported_protocols,
input_chdev: None,
})
}
}

// Sort the list by name
rcdev.sort_by(|a, b| a.name.cmp(&b.name));

Ok(rcdev)
}

struct UEvent {
name: String,
drv_name: String,
dev_name: String,
}

fn read_uevent(path: &Path) -> io::Result<UEvent> {
let mut name = String::new();
let mut drv_name = String::new();
let mut dev_name = String::new();

for line in fs::read_to_string(path.join("uevent"))?.lines() {
match line.split_once('=') {
Some(("NAME", value)) => {
value.clone_into(&mut name);
}
Some(("DRV_NAME", value)) => {
value.clone_into(&mut drv_name);
rcdev.push(Rcdev {
name: entry.file_name().to_str().unwrap().to_owned(),
device_name: uevent.dev_name,
driver: uevent.drv_name,
default_keymap: uevent.name,
inputdev,
lircdev,
enabled_protocols,
supported_protocols,
input_chdev: None,
})
}
Some(("DEVNAME", value)) | Some(("DEV_NAME", value)) => {
value.clone_into(&mut dev_name);
}
_ => (),
}
}

Ok(UEvent {
name,
drv_name,
dev_name,
})
}
// Sort the list by name
rcdev.sort_by(|a, b| a.name.cmp(&b.name));

impl KeymapTable {
pub fn matches(&self, rcdev: &Rcdev) -> bool {
(self.driver == "*" || self.driver == rcdev.driver)
&& (self.table == "*" || self.table == rcdev.default_keymap)
Ok(rcdev)
}
}

impl Rcdev {
/// Set the enabled kernel protocols for this rc device. protocols are indices into
/// supported_protocols.
pub fn set_enabled_protocols(&mut self, protocols: &[usize]) -> Result<(), String> {
let string = if protocols.is_empty() {
"none".into()
Expand Down Expand Up @@ -222,6 +204,39 @@ impl Rcdev {
}
}

struct UEvent {
name: String,
drv_name: String,
dev_name: String,
}

fn read_uevent(path: &Path) -> io::Result<UEvent> {
let mut name = String::new();
let mut drv_name = String::new();
let mut dev_name = String::new();

for line in fs::read_to_string(path.join("uevent"))?.lines() {
match line.split_once('=') {
Some(("NAME", value)) => {
value.clone_into(&mut name);
}
Some(("DRV_NAME", value)) => {
value.clone_into(&mut drv_name);
}
Some(("DEVNAME", value)) | Some(("DEV_NAME", value)) => {
value.clone_into(&mut dev_name);
}
_ => (),
}
}

Ok(UEvent {
name,
drv_name,
dev_name,
})
}

#[cfg(test)]
mod tests {
use crate::rc_maps::parse_rc_maps_file;
Expand Down

0 comments on commit 692d9df

Please sign in to comment.