Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(RGB LED): Add mc_led source devices. #188

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions src/dbus/interface/source/led.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use std::error::Error;

use crate::udev::device::{AttributeGetter, AttributeSetter, UdevDevice};
use zbus::{fdo, Connection};
use zbus_macros::interface;

use crate::input::source::iio::get_dbus_path;

/// DBusInterface exposing information about a HIDRaw device
pub struct SourceLedInterface {
device: UdevDevice,
}

impl SourceLedInterface {
pub fn new(device: UdevDevice) -> SourceLedInterface {
SourceLedInterface { device }
}

/// Creates a new instance of the source hidraw interface on DBus. Returns
/// a structure with information about the source device.
pub async fn listen_on_dbus(
conn: Connection,
device: UdevDevice,
) -> Result<(), Box<dyn Error>> {
let iface = SourceLedInterface::new(device);
let Ok(id) = iface.id() else {
return Ok(());
};
let path = get_dbus_path(id);

tokio::task::spawn(async move {
log::debug!("Starting dbus interface: {path}");
let result = conn.object_server().at(path.clone(), iface).await;
if let Err(e) = result {
log::debug!("Failed to start dbus interface {path}: {e:?}");
} else {
log::debug!("Started dbus interface: {path}");
}
});
Ok(())
}
}

#[interface(name = "org.shadowblip.Input.Source.LEDDevice")]
impl SourceLedInterface {
/// Returns the human readable name of the device (e.g. XBox 360 Pad)
#[zbus(property)]
fn id(&self) -> fdo::Result<String> {
Ok(self.device.sysname())
}
}
1 change: 1 addition & 0 deletions src/dbus/interface/source/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub mod evdev;
pub mod hidraw;
pub mod iio_imu;
pub mod led;
51 changes: 48 additions & 3 deletions src/input/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ use crate::dbus::interface::manager::ManagerInterface;
use crate::dbus::interface::source::evdev::SourceEventDeviceInterface;
use crate::dbus::interface::source::hidraw::SourceHIDRawInterface;
use crate::dbus::interface::source::iio_imu::SourceIioImuInterface;
use crate::dbus::interface::source::led::SourceLedInterface;
use crate::dmi::data::DMIData;
use crate::dmi::get_cpu_info;
use crate::dmi::get_dmi_data;
use crate::input::composite_device::CompositeDevice;
use crate::input::source::evdev;
use crate::input::source::hidraw;
use crate::input::source::iio;
use crate::input::source::led;
use crate::input::target::TargetDevice;
use crate::input::target::TargetDeviceTypeId;
use crate::udev;
Expand Down Expand Up @@ -991,10 +993,8 @@ impl Manager {
async fn on_device_added(&mut self, device: UdevDevice) -> Result<(), Box<dyn Error>> {
// We REQUIRE a dev node
let dev_node = device.devnode();
if dev_node.is_empty() {
return Ok(());
}
let sys_name = device.sysname();
log::debug!("sysname is empty for {:?}", device.syspath());
if sys_name.is_empty() {
return Ok(());
}
Expand All @@ -1012,6 +1012,10 @@ impl Manager {
// Create a DBus interface depending on the device subsystem
match subsystem.as_str() {
"input" => {
if dev_node.is_empty() {
log::debug!("devnode is empty for {:?}", device.syspath());
return Ok(());
}
log::debug!("Event device added");

// Create a DBus interface for the event device
Expand Down Expand Up @@ -1067,6 +1071,10 @@ impl Manager {
log::debug!("Finished adding {id}");
}
"hidraw" => {
if dev_node.is_empty() {
log::debug!("devnode is empty for {:?}", device.syspath());
return Ok(());
}
log::debug!("hidraw device added");
// Create a DBus interface for the event device
let conn = self.dbus.clone();
Expand Down Expand Up @@ -1151,6 +1159,10 @@ impl Manager {
}

"iio" => {
if dev_node.is_empty() {
log::debug!("devnode is empty for {:?}", device.syspath());
return Ok(());
}
log::debug!("iio device added");

// Create a DBus interface for the event device
Expand Down Expand Up @@ -1181,6 +1193,36 @@ impl Manager {
self.on_source_device_added(id.clone(), device).await?;
log::debug!("Finished adding event device {id}");
}
"leds" => {
log::debug!("LED device added");
// Create a DBus interface for the event device
let conn = self.dbus.clone();
log::debug!("Attempting to listen on dbus for {dev_node} | {sysname}");
task::spawn(async move {
let result = SourceLedInterface::listen_on_dbus(conn, dev).await;
if let Err(e) = result {
log::error!("Error creating source evdev dbus interface: {e:?}");
}
log::debug!("Finished adding source device on dbus");
});

// Add the device as a source device
let path = led::get_dbus_path(sys_name.clone());
self.source_device_dbus_paths.insert(id.clone(), path);

// Check to see if the device is virtual
if device.is_virtual() {
log::debug!("{} is virtual, skipping consideration.", dev_node);
return Ok(());
} else {
log::trace!("Real device: {}", dev_node);
}

// Signal that a source device was added
log::debug!("Spawing task to add source device: {id}");
self.on_source_device_added(id.clone(), device).await?;
log::debug!("Finished adding event device {id}");
}

_ => {
return Err(format!("Device subsystem not supported: {subsystem:?}").into());
Expand Down Expand Up @@ -1286,6 +1328,9 @@ impl Manager {
let iio_devices = udev::discover_devices("iio")?;
let iio_devices = iio_devices.into_iter().map(|dev| dev.into()).collect();
Manager::discover_devices(&cmd_tx, iio_devices).await?;
let led_devices = udev::discover_devices("leds")?;
let led_devices = led_devices.into_iter().map(|dev| dev.into()).collect();
Manager::discover_devices(&cmd_tx, led_devices).await?;

// Watch for IIO device events.
task::spawn_blocking(move || {
Expand Down
63 changes: 63 additions & 0 deletions src/input/source/led.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
pub mod multicolor_chassis;

use std::error::Error;

//use glob_match::glob_match;

use crate::{
config, constants::BUS_SOURCES_PREFIX, input::composite_device::client::CompositeDeviceClient,
udev::device::UdevDevice,
};

use self::multicolor_chassis::MultiColorChassis;

use super::SourceDriver;

/// List of available drivers
enum DriverType {
Unknown,
MultiColorChassis,
}

/// [IioDevice] represents an input device using the iio subsystem.
#[derive(Debug)]
pub enum LedDevice {
MultiColorChassis(SourceDriver<MultiColorChassis>),
}

impl LedDevice {
/// Create a new [IioDevice] associated with the given device and
/// composite device. The appropriate driver will be selected based on
/// the provided device.
pub fn new(
device_info: UdevDevice,
composite_device: CompositeDeviceClient,
config: Option<config::IIO>,
) -> Result<Self, Box<dyn Error + Send + Sync>> {
let driver_type = LedDevice::get_driver_type(&device_info);

match driver_type {
DriverType::Unknown => Err("No driver for LED interface found".into()),
DriverType::MultiColorChassis => {
let device = MultiColorChassis::new(device_info.clone())?;
let source_device = SourceDriver::new(composite_device, device, device_info);
Ok(Self::MultiColorChassis(source_device))
}
}
}

/// Return the driver type for the given device info
fn get_driver_type(device: &UdevDevice) -> DriverType {
let device_name = device.name();
let name = device_name.as_str();
log::debug!("Finding driver for LED interface: {name}");
// Unknown
DriverType::Unknown
}
}

/// Returns the DBus path for an [IIODevice] from a device id (E.g. iio:device0)
pub fn get_dbus_path(id: String) -> String {
let name = id.replace(':', "_");
format!("{}/{}", BUS_SOURCES_PREFIX, name)
}
24 changes: 24 additions & 0 deletions src/input/source/led/multicolor_chassis.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::{error::Error, fmt::Debug};

use crate::{input::source::SourceOutputDevice, udev::device::UdevDevice};

/// OrangePi Neo Touchpad source device implementation
pub struct MultiColorChassis {
device_info: UdevDevice,
}

impl MultiColorChassis {
/// Create a new OrangePi Neo touchscreen source device with the given udev
/// device information
pub fn new(device_info: UdevDevice) -> Result<Self, Box<dyn Error + Send + Sync>> {
Ok(Self { device_info })
}
}

impl SourceOutputDevice for MultiColorChassis {}

impl Debug for MultiColorChassis {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MultiColorChassis").finish()
}
}
1 change: 1 addition & 0 deletions src/input/source/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ pub mod command;
pub mod evdev;
pub mod hidraw;
pub mod iio;
pub mod led;

/// Size of the [SourceCommand] buffer for receiving output events
const BUFFER_SIZE: usize = 2048;
Expand Down
6 changes: 3 additions & 3 deletions src/udev/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,9 @@ pub fn discover_devices(subsystem: &str) -> Result<Vec<udev::Device>, Box<dyn Er
let mut node_devices = Vec::new();
let devices = enumerator.scan_devices()?;
for device in devices {
let Some(_) = device.devnode() else {
log::trace!("No devnode found for device: {:?}", device);
continue;
if device.devnode().is_none() {
log::debug!("No devnode found for device: {:?}", device.sysname());
// continue;
};

let name = device.sysname();
Expand Down
Loading