Files
RedBear-OS/drivers/input/i2c-hidd/src/main.rs
T

115 lines
3.5 KiB
Rust

use std::process;
use std::thread;
use std::time::Duration;
use anyhow::{Context, Result};
mod acpi;
mod hid;
mod input;
mod quirks;
use acpi::{
hid_descriptor_address, prepare_acpi_device, read_decoded_resources, recover_acpi_device,
scan_acpi_i2c_hid_devices,
};
use hid::{fetch_hid_descriptor, fetch_report_descriptor, stream_input_reports, I2cAdapterClient};
use input::InputForwarder;
use quirks::match_probe_failure_quirk;
fn main() {
daemon::Daemon::new(daemon);
}
fn daemon(daemon: daemon::Daemon) -> ! {
common::setup_logging(
"input",
"i2c-hid",
"i2c-hidd",
common::output_level(),
common::file_level(),
);
if let Err(err) = run(daemon) {
log::error!("RB_I2C_HIDD_BLOCKER stage=startup error={err:#}");
process::exit(1);
}
process::exit(0);
}
fn run(daemon: daemon::Daemon) -> Result<()> {
log::info!("RB_I2C_HIDD_SCHEMA version=1");
let devices = scan_acpi_i2c_hid_devices().context("failed to scan ACPI I2C HID devices")?;
if devices.is_empty() {
log::warn!("RB_I2C_HIDD_BLOCKER stage=scan error=no PNP0C50/ACPI0C50 devices found");
}
let mut workers = Vec::new();
for device in devices {
log::info!("RB_I2C_HIDD_SNAPSHOT device={device}");
workers.push(thread::spawn(move || {
if let Err(err) = bind_device(&device) {
log::error!("RB_I2C_HIDD_BLOCKER device={} error={:#}", device, err);
}
}));
}
daemon.ready();
if workers.is_empty() {
loop {
thread::sleep(Duration::from_secs(5));
}
}
for worker in workers {
let _ = worker.join();
}
Ok(())
}
pub fn bind_device(device_path: &str) -> Result<()> {
prepare_acpi_device(device_path)
.with_context(|| format!("failed to prepare ACPI device {device_path}"))?;
let resources = read_decoded_resources(device_path)
.with_context(|| format!("failed to decode _CRS for {device_path}"))?;
log::info!(
"RB_I2C_HIDD_SNAPSHOT device={} adapter={} addr={:04x} irq={:?} gpio_int={} gpio_io={}",
device_path,
resources.i2c.adapter,
resources.i2c.address,
resources.irq,
resources.gpio_int.len(),
resources.gpio_io.len()
);
let hid_desc_addr = hid_descriptor_address(device_path)
.with_context(|| format!("failed to evaluate _DSM for {device_path}"))?;
let adapter = I2cAdapterClient::new(resources.i2c.clone());
let hid_desc = fetch_hid_descriptor(&adapter, resources.i2c.address, hid_desc_addr)
.with_context(|| format!("failed to fetch HID descriptor for {device_path}"))?;
let report_desc = fetch_report_descriptor(&adapter, resources.i2c.address, &hid_desc)
.with_context(|| format!("failed to fetch report descriptor for {device_path}"))?;
let mut forwarder = InputForwarder::new().context("failed to connect to inputd producer")?;
match stream_input_reports(
&adapter,
resources.i2c.address,
&hid_desc,
&report_desc,
&mut forwarder,
) {
Ok(()) => Ok(()),
Err(err) => {
let quirk =
match_probe_failure_quirk().context("failed to evaluate DMI recovery quirks")?;
recover_acpi_device(device_path, &resources, quirk.as_ref())
.with_context(|| format!("failed ACPI recovery for {device_path}"))?;
Err(err).with_context(|| format!("streaming input reports failed for {device_path}"))
}
}
}