//! ATX data types and structures //! //! Defines the configuration and state types for the flexible ATX power control system. //! Each ATX action (power, reset) can be independently configured with different hardware. use serde::{Deserialize, Serialize}; use typeshare::typeshare; /// Power status #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum PowerStatus { /// Power is on On, /// Power is off Off, /// Power status unknown (no LED connected) Unknown, } impl Default for PowerStatus { fn default() -> Self { Self::Unknown } } /// Driver type for ATX key operations #[typeshare] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AtxDriverType { /// GPIO control via Linux character device Gpio, /// USB HID relay module UsbRelay, /// Disabled / Not configured None, } impl Default for AtxDriverType { fn default() -> Self { Self::None } } /// Active level for GPIO pins #[typeshare] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum ActiveLevel { /// Active high (default for most cases) High, /// Active low (inverted) Low, } impl Default for ActiveLevel { fn default() -> Self { Self::High } } /// Configuration for a single ATX key (power or reset) /// This is the "four-tuple" configuration: (driver, device, pin/channel, level) #[typeshare] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(default)] pub struct AtxKeyConfig { /// Driver type (GPIO or USB Relay) pub driver: AtxDriverType, /// Device path: /// - For GPIO: /dev/gpiochipX /// - For USB Relay: /dev/hidrawX pub device: String, /// Pin or channel number: /// - For GPIO: GPIO pin number /// - For USB Relay: relay channel (0-based) pub pin: u32, /// Active level (only applicable to GPIO, ignored for USB Relay) pub active_level: ActiveLevel, } impl Default for AtxKeyConfig { fn default() -> Self { Self { driver: AtxDriverType::None, device: String::new(), pin: 0, active_level: ActiveLevel::High, } } } impl AtxKeyConfig { /// Check if this key is configured pub fn is_configured(&self) -> bool { self.driver != AtxDriverType::None && !self.device.is_empty() } } /// LED sensing configuration (optional) #[typeshare] #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(default)] pub struct AtxLedConfig { /// Whether LED sensing is enabled pub enabled: bool, /// GPIO chip for LED sensing pub gpio_chip: String, /// GPIO pin for LED input pub gpio_pin: u32, /// Whether LED is active low (inverted logic) pub inverted: bool, } impl Default for AtxLedConfig { fn default() -> Self { Self { enabled: false, gpio_chip: String::new(), gpio_pin: 0, inverted: false, } } } impl AtxLedConfig { /// Check if LED sensing is configured pub fn is_configured(&self) -> bool { self.enabled && !self.gpio_chip.is_empty() } } /// ATX state information #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AtxState { /// Whether ATX feature is available/enabled pub available: bool, /// Whether power button is configured pub power_configured: bool, /// Whether reset button is configured pub reset_configured: bool, /// Current power status pub power_status: PowerStatus, /// Whether power LED sensing is supported pub led_supported: bool, } impl Default for AtxState { fn default() -> Self { Self { available: false, power_configured: false, reset_configured: false, power_status: PowerStatus::Unknown, led_supported: false, } } } /// ATX power action request #[derive(Debug, Clone, Deserialize)] pub struct AtxPowerRequest { /// Action to perform: "short", "long", "reset" pub action: AtxAction, } /// ATX power action #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[serde(rename_all = "lowercase")] pub enum AtxAction { /// Short press power button (turn on or graceful shutdown) Short, /// Long press power button (force power off) Long, /// Press reset button Reset, } /// Available ATX devices for discovery #[typeshare] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AtxDevices { /// Available GPIO chips (/dev/gpiochip*) pub gpio_chips: Vec, /// Available USB HID relay devices (/dev/hidraw*) pub usb_relays: Vec, } impl Default for AtxDevices { fn default() -> Self { Self { gpio_chips: Vec::new(), usb_relays: Vec::new(), } } } #[cfg(test)] mod tests { use super::*; #[test] fn test_power_status_default() { assert_eq!(PowerStatus::default(), PowerStatus::Unknown); } #[test] fn test_atx_driver_type_default() { assert_eq!(AtxDriverType::default(), AtxDriverType::None); } #[test] fn test_active_level_default() { assert_eq!(ActiveLevel::default(), ActiveLevel::High); } #[test] fn test_atx_key_config_default() { let config = AtxKeyConfig::default(); assert_eq!(config.driver, AtxDriverType::None); assert!(config.device.is_empty()); assert_eq!(config.pin, 0); assert!(!config.is_configured()); } #[test] fn test_atx_key_config_is_configured() { let mut config = AtxKeyConfig::default(); assert!(!config.is_configured()); config.driver = AtxDriverType::Gpio; assert!(!config.is_configured()); // device still empty config.device = "/dev/gpiochip0".to_string(); assert!(config.is_configured()); config.driver = AtxDriverType::None; assert!(!config.is_configured()); // driver is None } #[test] fn test_atx_led_config_default() { let config = AtxLedConfig::default(); assert!(!config.enabled); assert!(config.gpio_chip.is_empty()); assert!(!config.is_configured()); } #[test] fn test_atx_led_config_is_configured() { let mut config = AtxLedConfig::default(); assert!(!config.is_configured()); config.enabled = true; assert!(!config.is_configured()); // gpio_chip still empty config.gpio_chip = "/dev/gpiochip0".to_string(); assert!(config.is_configured()); } #[test] fn test_atx_state_default() { let state = AtxState::default(); assert!(!state.available); assert!(!state.power_configured); assert!(!state.reset_configured); assert_eq!(state.power_status, PowerStatus::Unknown); } }