diff --git a/Cargo.lock b/Cargo.lock index 3acdfc114..316b90384 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -209,7 +209,7 @@ dependencies = [ name = "battery-service" version = "0.1.0" dependencies = [ - "battery-service-messages", + "battery-service-interface", "defmt 0.3.100", "embassy-futures", "embassy-sync", @@ -218,18 +218,25 @@ dependencies = [ "embedded-hal 1.0.0", "embedded-hal-async", "embedded-services", - "heapless", "log", - "mctp-rs", "odp-service-common", "power-policy-interface", - "zerocopy", ] [[package]] -name = "battery-service-messages" +name = "battery-service-interface" +version = "0.1.0" +dependencies = [ + "defmt 0.3.100", + "embedded-batteries-async", + "log", +] + +[[package]] +name = "battery-service-relay" version = "0.1.0" dependencies = [ + "battery-service-interface", "defmt 0.3.100", "embedded-batteries-async", "embedded-services", diff --git a/Cargo.toml b/Cargo.toml index d3558a57d..b78043344 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,8 @@ resolver = "2" members = [ "battery-service", - "battery-service-messages", + "battery-service-interface", + "battery-service-relay", "thermal-service", "thermal-service-messages", "cfu-service", @@ -55,7 +56,8 @@ unwrap_used = "deny" [workspace.dependencies] aligned = "0.4" anyhow = "1.0" -battery-service-messages = { path = "./battery-service-messages" } +battery-service-interface = { path = "./battery-service-interface" } +battery-service-relay = { path = "./battery-service-relay" } bitfield = "0.17.0" bitflags = "2.8.0" bitvec = { version = "1.0.1", default-features = false } @@ -74,7 +76,7 @@ embassy-imxrt = { git = "https://github.com/OpenDevicePartnership/embassy-imxrt" embassy-sync = "0.7.2" embassy-time = "0.5.0" embassy-time-driver = "0.2.1" -embedded-batteries-async = "0.3" +embedded-batteries-async = "0.3.4" cfu-service = { path = "./cfu-service" } embedded-cfu-protocol = { git = "https://github.com/OpenDevicePartnership/embedded-cfu" } embedded-hal = "1.0" diff --git a/battery-service-interface/Cargo.toml b/battery-service-interface/Cargo.toml new file mode 100644 index 000000000..f73417a88 --- /dev/null +++ b/battery-service-interface/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "battery-service-interface" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +defmt = { workspace = true, optional = true } +log = { workspace = true, optional = true } +embedded-batteries-async.workspace = true + +[lints] +workspace = true + +[features] +defmt = ["dep:defmt", "embedded-batteries-async/defmt"] +log = ["dep:log"] diff --git a/battery-service-interface/src/lib.rs b/battery-service-interface/src/lib.rs new file mode 100644 index 000000000..f93f5caab --- /dev/null +++ b/battery-service-interface/src/lib.rs @@ -0,0 +1,202 @@ +#![no_std] + +pub use embedded_batteries_async::acpi::{ + BatteryState, BatterySwapCapability, BatteryTechnology, Bct, BctReturnResult, Bma, Bmc, BmcControlFlags, Bmd, + BmdCapabilityFlags, BmdStatusFlags, Bms, Bpc, Bps, Bpt, BstReturn, Btm, BtmReturnResult, Btp, PowerSource, + PowerSourceState, PowerThresholdSupport, PowerUnit, PsrReturn, StaReturn, +}; + +/// Standard Battery Service Model Number String Size +pub const STD_BIX_MODEL_SIZE: usize = 8; +/// Standard Battery Service Serial Number String Size +pub const STD_BIX_SERIAL_SIZE: usize = 8; +/// Standard Battery Service Battery Type String Size +pub const STD_BIX_BATTERY_SIZE: usize = 8; +/// Standard Battery Service OEM Info String Size +pub const STD_BIX_OEM_SIZE: usize = 8; +/// Standard Power Policy Service Model Number String Size +pub const STD_PIF_MODEL_SIZE: usize = 8; +/// Standard Power Policy Serial Number String Size +pub const STD_PIF_SERIAL_SIZE: usize = 8; +/// Standard Power Policy Service OEM Info String Size +pub const STD_PIF_OEM_SIZE: usize = 8; + +#[derive(PartialEq, Clone, Copy, Default)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct BixFixedStrings { + /// Revision of the BIX structure. Current revision is 1. + pub revision: u32, + /// Unit used for capacity and rate values. + pub power_unit: PowerUnit, + /// Design capacity of the battery (in mWh or mAh). + pub design_capacity: u32, + /// Last full charge capacity (in mWh or mAh). + pub last_full_charge_capacity: u32, + /// Battery technology type. + pub battery_technology: BatteryTechnology, + /// Design voltage (in mV). + pub design_voltage: u32, + /// Warning capacity threshold (in mWh or mAh). + pub design_cap_of_warning: u32, + /// Low capacity threshold (in mWh or mAh). + pub design_cap_of_low: u32, + /// Number of charge/discharge cycles. + pub cycle_count: u32, + /// Measurement accuracy in thousandths of a percent (e.g., 80000 = 80.000%). + pub measurement_accuracy: u32, + /// Maximum supported sampling time (in ms). + pub max_sampling_time: u32, + /// Minimum supported sampling time (in ms). + pub min_sampling_time: u32, + /// Maximum supported averaging interval (in ms). + pub max_averaging_interval: u32, + /// Minimum supported averaging interval (in ms). + pub min_averaging_interval: u32, + /// Capacity granularity between low and warning (in mWh or mAh). + pub battery_capacity_granularity_1: u32, + /// Capacity granularity between warning and full (in mWh or mAh). + pub battery_capacity_granularity_2: u32, + /// OEM-specific model number (ASCIIZ). + pub model_number: [u8; STD_BIX_MODEL_SIZE], + /// OEM-specific serial number (ASCIIZ). + pub serial_number: [u8; STD_BIX_SERIAL_SIZE], + /// OEM-specific battery type (ASCIIZ). + pub battery_type: [u8; STD_BIX_BATTERY_SIZE], + /// OEM-specific information (ASCIIZ). + pub oem_info: [u8; STD_BIX_OEM_SIZE], + /// Battery swapping capability. + pub battery_swapping_capability: BatterySwapCapability, +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct PifFixedStrings { + /// Bitfield describing the state and characteristics of the power source. + pub power_source_state: PowerSourceState, + /// Maximum rated output power in milliwatts (mW). + /// + /// 0xFFFFFFFF indicates the value is unavailable. + pub max_output_power: u32, + /// Maximum rated input power in milliwatts (mW). + /// + /// 0xFFFFFFFF indicates the value is unavailable. + pub max_input_power: u32, + /// OEM-specific model number (ASCIIZ). Empty string if not supported. + pub model_number: [u8; STD_PIF_MODEL_SIZE], + /// OEM-specific serial number (ASCIIZ). Empty string if not supported. + pub serial_number: [u8; STD_PIF_SERIAL_SIZE], + /// OEM-specific information (ASCIIZ). Empty string if not supported. + pub oem_info: [u8; STD_PIF_OEM_SIZE], +} + +/// Fuel gauge ID +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub struct DeviceId(pub u8); + +pub trait BatteryService { + /// Queries the estimated time remaining until the battery reaches the specified charge level. Corresponds to ACPI's _BCT method + fn battery_charge_time( + &self, + battery_id: DeviceId, + charge_level: Bct, + ) -> impl core::future::Future>; + + /// Returns static information about the battery. Corresponds to ACPI's _BIX method. + fn battery_info( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; + + /// Sets the averaging interval of battery capacity measurement in milliseconds. Corresponds to ACPI's _BMA method. + fn set_battery_measurement_averaging_interval( + &self, + battery_id: DeviceId, + bma: Bma, + ) -> impl core::future::Future>; + + /// Battery maintenance control. Corresponds to ACPI's _BMC method. + fn battery_maintenance_control( + &self, + battery_id: DeviceId, + bmc: Bmc, + ) -> impl core::future::Future>; + + /// Retrieves battery maintenance data. Corresponds to ACPI's _BMD method. + fn battery_maintenance_data( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; + + /// Sets the battery measurement sampling time in milliseconds. Corresponds to ACPI's _BMS method. + fn set_battery_measurement_sampling_time( + &self, + battery_id: DeviceId, + battery_measurement_sampling: Bms, + ) -> impl core::future::Future>; + + /// Queries the current power characteristics of the battery. Corresponds to ACPI's _BPC method. + fn battery_power_characteristics( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; + + /// Queries the current state of the battery. Corresponds to ACPI's _BPS method. + fn battery_power_state( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; + + /// Sets battery power threshold. Corresponds to ACPI's _BPT method. + fn set_battery_power_threshold( + &self, + battery_id: DeviceId, + power_threshold: Bpt, + ) -> impl core::future::Future>; + + /// Queries the battery's current estimated remaining capacity. Corresponds to ACPI's _BST method. + fn battery_status( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; + + /// Queries the estimated time remaining until the battery is fully discharged at the current discharge rate. Corresponds to ACPI's _BTM method. + fn battery_time_to_empty( + &self, + battery_id: DeviceId, + battery_discharge_rate: Btm, + ) -> impl core::future::Future>; + + /// Sets a battery trip point. Corresponds to ACPI's _BTP method. + fn set_battery_trip_point( + &self, + battery_id: DeviceId, + btp: Btp, + ) -> impl core::future::Future>; + + /// Queries whether the battery is currently in use (i.e., providing power to the system). Corresponds to ACPI's _PSR method. + fn is_in_use(&self, battery_id: DeviceId) -> impl core::future::Future>; + + /// Queries information about the battery's power source. Corresponds to ACPI's _PIF method. + fn power_source_information( + &self, + power_source_id: DeviceId, + ) -> impl core::future::Future>; + + /// Queries the battery's status. Corresponds to ACPI's _STA method. + fn device_status( + &self, + battery_id: DeviceId, + ) -> impl core::future::Future>; +} + +#[derive(Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// Errors that can occur when interacting with the battery service. +pub enum BatteryError { + /// The specified battery ID does not correspond to any known battery. + UnknownDeviceId, + + /// An unknown error occurred while processing the request. + UnspecifiedFailure, +} diff --git a/battery-service-messages/src/lib.rs b/battery-service-messages/src/lib.rs deleted file mode 100644 index c00bf352e..000000000 --- a/battery-service-messages/src/lib.rs +++ /dev/null @@ -1,677 +0,0 @@ -#![no_std] - -use embedded_batteries_async::acpi::ThresholdId; -pub use embedded_batteries_async::acpi::{ - BatteryState, BatterySwapCapability, BatteryTechnology, Bct, BctReturnResult, Bma, Bmc, BmcControlFlags, Bmd, - BmdCapabilityFlags, BmdStatusFlags, Bms, Bpc, Bps, Bpt, BstReturn, Btm, BtmReturnResult, Btp, PowerSource, - PowerSourceState, PowerThresholdSupport, PowerUnit, PsrReturn, StaReturn, -}; -use embedded_services::relay::{MessageSerializationError, SerializableMessage}; - -// Unfortunately `TryFrom` is not implemented by embedded-batteries for these types - -/// Attempt to convert a `u32` to a `PowerUnit`. -pub fn power_unit_try_from_u32(value: u32) -> Result { - match value { - 0 => Ok(PowerUnit::MilliWatts), - 1 => Ok(PowerUnit::MilliAmps), - _ => Err(MessageSerializationError::InvalidPayload("Invalid PowerUnit")), - } -} - -/// Attempt to convert a `u32` to a `BatteryTechnology`. -pub fn bat_tech_try_from_u32(value: u32) -> Result { - match value { - 0 => Ok(BatteryTechnology::Primary), - 1 => Ok(BatteryTechnology::Secondary), - _ => Err(MessageSerializationError::InvalidPayload("Invalid BatteryTechnology")), - } -} - -/// Attempt to convert a `u32` to a `BatterySwapCapability`. -pub fn bat_swap_try_from_u32(value: u32) -> Result { - match value { - 0 => Ok(BatterySwapCapability::NonSwappable), - 1 => Ok(BatterySwapCapability::ColdSwappable), - 2 => Ok(BatterySwapCapability::HotSwappable), - _ => Err(MessageSerializationError::InvalidPayload("Invalid BatteryTechnology")), - } -} - -/// Attempt to convert a `u32` to a `ThresholdId`. -pub fn thres_id_try_from_u32(value: u32) -> Result { - match value { - 0 => Ok(ThresholdId::ClearAll), - 1 => Ok(ThresholdId::InstantaneousPeakPower), - 2 => Ok(ThresholdId::SustainablePeakPower), - _ => Err(MessageSerializationError::InvalidPayload("Invalid ThresholdId")), - } -} - -/// Attempt to convert a `u32` to a `PowerSource`. -pub fn pwr_src_try_from_u32(value: u32) -> Result { - match value { - 0 => Ok(PowerSource::Offline), - 1 => Ok(PowerSource::Online), - _ => Err(MessageSerializationError::InvalidPayload("Invalid PowerSource")), - } -} - -/// Standard Battery Service Model Number String Size -pub const STD_BIX_MODEL_SIZE: usize = 8; -/// Standard Battery Service Serial Number String Size -pub const STD_BIX_SERIAL_SIZE: usize = 8; -/// Standard Battery Service Battery Type String Size -pub const STD_BIX_BATTERY_SIZE: usize = 8; -/// Standard Battery Service OEM Info String Size -pub const STD_BIX_OEM_SIZE: usize = 8; -/// Standard Power Policy Service Model Number String Size -pub const STD_PIF_MODEL_SIZE: usize = 8; -/// Standard Power Policy Serial Number String Size -pub const STD_PIF_SERIAL_SIZE: usize = 8; -/// Standard Power Policy Service OEM Info String Size -pub const STD_PIF_OEM_SIZE: usize = 8; - -#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Copy, Clone, Debug, PartialEq)] -#[repr(u16)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -/// ACPI Battery Methods -enum BatteryCmd { - /// Battery Information eXtended - GetBix = 1, - /// Battery Status - GetBst = 2, - /// Power Source - GetPsr = 3, - /// Power source InFormation - GetPif = 4, - /// Battery Power State - GetBps = 5, - /// Battery Trip Point - SetBtp = 6, - /// Battery Power Threshold - SetBpt = 7, - /// Battery Power Characteristics - GetBpc = 8, - /// Battery Maintenance Control - SetBmc = 9, - /// Battery Maintenance Data - GetBmd = 10, - /// Battery Charge Time - GetBct = 11, - /// Battery Time - GetBtm = 12, - /// Battery Measurement Sampling Time - SetBms = 13, - /// Battery Measurement Averaging Interval - SetBma = 14, - /// Device Status - GetSta = 15, -} - -impl From<&AcpiBatteryRequest> for BatteryCmd { - fn from(request: &AcpiBatteryRequest) -> Self { - match request { - AcpiBatteryRequest::BatteryGetBixRequest { .. } => BatteryCmd::GetBix, - AcpiBatteryRequest::BatteryGetBstRequest { .. } => BatteryCmd::GetBst, - AcpiBatteryRequest::BatteryGetPsrRequest { .. } => BatteryCmd::GetPsr, - AcpiBatteryRequest::BatteryGetPifRequest { .. } => BatteryCmd::GetPif, - AcpiBatteryRequest::BatteryGetBpsRequest { .. } => BatteryCmd::GetBps, - AcpiBatteryRequest::BatterySetBtpRequest { .. } => BatteryCmd::SetBtp, - AcpiBatteryRequest::BatterySetBptRequest { .. } => BatteryCmd::SetBpt, - AcpiBatteryRequest::BatteryGetBpcRequest { .. } => BatteryCmd::GetBpc, - AcpiBatteryRequest::BatterySetBmcRequest { .. } => BatteryCmd::SetBmc, - AcpiBatteryRequest::BatteryGetBmdRequest { .. } => BatteryCmd::GetBmd, - AcpiBatteryRequest::BatteryGetBctRequest { .. } => BatteryCmd::GetBct, - AcpiBatteryRequest::BatteryGetBtmRequest { .. } => BatteryCmd::GetBtm, - AcpiBatteryRequest::BatterySetBmsRequest { .. } => BatteryCmd::SetBms, - AcpiBatteryRequest::BatterySetBmaRequest { .. } => BatteryCmd::SetBma, - AcpiBatteryRequest::BatteryGetStaRequest { .. } => BatteryCmd::GetSta, - } - } -} - -impl From<&AcpiBatteryResponse> for BatteryCmd { - fn from(response: &AcpiBatteryResponse) -> Self { - match response { - AcpiBatteryResponse::BatteryGetBixResponse { .. } => BatteryCmd::GetBix, - AcpiBatteryResponse::BatteryGetBstResponse { .. } => BatteryCmd::GetBst, - AcpiBatteryResponse::BatteryGetPsrResponse { .. } => BatteryCmd::GetPsr, - AcpiBatteryResponse::BatteryGetPifResponse { .. } => BatteryCmd::GetPif, - AcpiBatteryResponse::BatteryGetBpsResponse { .. } => BatteryCmd::GetBps, - AcpiBatteryResponse::BatterySetBtpResponse { .. } => BatteryCmd::SetBtp, - AcpiBatteryResponse::BatterySetBptResponse { .. } => BatteryCmd::SetBpt, - AcpiBatteryResponse::BatteryGetBpcResponse { .. } => BatteryCmd::GetBpc, - AcpiBatteryResponse::BatterySetBmcResponse { .. } => BatteryCmd::SetBmc, - AcpiBatteryResponse::BatteryGetBmdResponse { .. } => BatteryCmd::GetBmd, - AcpiBatteryResponse::BatteryGetBctResponse { .. } => BatteryCmd::GetBct, - AcpiBatteryResponse::BatteryGetBtmResponse { .. } => BatteryCmd::GetBtm, - AcpiBatteryResponse::BatterySetBmsResponse { .. } => BatteryCmd::SetBms, - AcpiBatteryResponse::BatterySetBmaResponse { .. } => BatteryCmd::SetBma, - AcpiBatteryResponse::BatteryGetStaResponse { .. } => BatteryCmd::GetSta, - } - } -} - -const BIX_MODEL_NUM_START_IDX: usize = 64; -const BIX_MODEL_NUM_END_IDX: usize = BIX_MODEL_NUM_START_IDX + STD_BIX_MODEL_SIZE; -const BIX_SERIAL_NUM_START_IDX: usize = BIX_MODEL_NUM_END_IDX; -const BIX_SERIAL_NUM_END_IDX: usize = BIX_SERIAL_NUM_START_IDX + STD_BIX_SERIAL_SIZE; -const BIX_BATTERY_TYPE_START_IDX: usize = BIX_SERIAL_NUM_END_IDX; -const BIX_BATTERY_TYPE_END_IDX: usize = BIX_BATTERY_TYPE_START_IDX + STD_BIX_BATTERY_SIZE; -const BIX_OEM_INFO_START_IDX: usize = BIX_BATTERY_TYPE_END_IDX; -const BIX_OEM_INFO_END_IDX: usize = BIX_OEM_INFO_START_IDX + STD_BIX_OEM_SIZE; - -#[derive(PartialEq, Clone, Copy, Default)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct BixFixedStrings { - /// Revision of the BIX structure. Current revision is 1. - pub revision: u32, - /// Unit used for capacity and rate values. - pub power_unit: PowerUnit, - /// Design capacity of the battery (in mWh or mAh). - pub design_capacity: u32, - /// Last full charge capacity (in mWh or mAh). - pub last_full_charge_capacity: u32, - /// Battery technology type. - pub battery_technology: BatteryTechnology, - /// Design voltage (in mV). - pub design_voltage: u32, - /// Warning capacity threshold (in mWh or mAh). - pub design_cap_of_warning: u32, - /// Low capacity threshold (in mWh or mAh). - pub design_cap_of_low: u32, - /// Number of charge/discharge cycles. - pub cycle_count: u32, - /// Measurement accuracy in thousandths of a percent (e.g., 80000 = 80.000%). - pub measurement_accuracy: u32, - /// Maximum supported sampling time (in ms). - pub max_sampling_time: u32, - /// Minimum supported sampling time (in ms). - pub min_sampling_time: u32, - /// Maximum supported averaging interval (in ms). - pub max_averaging_interval: u32, - /// Minimum supported averaging interval (in ms). - pub min_averaging_interval: u32, - /// Capacity granularity between low and warning (in mWh or mAh). - pub battery_capacity_granularity_1: u32, - /// Capacity granularity between warning and full (in mWh or mAh). - pub battery_capacity_granularity_2: u32, - /// OEM-specific model number (ASCIIZ). - pub model_number: [u8; STD_BIX_MODEL_SIZE], - /// OEM-specific serial number (ASCIIZ). - pub serial_number: [u8; STD_BIX_SERIAL_SIZE], - /// OEM-specific battery type (ASCIIZ). - pub battery_type: [u8; STD_BIX_BATTERY_SIZE], - /// OEM-specific information (ASCIIZ). - pub oem_info: [u8; STD_BIX_OEM_SIZE], - /// Battery swapping capability. - pub battery_swapping_capability: BatterySwapCapability, -} - -// TODO this is essentially a hand-written reinterpret_cast - can we codegen some of this instead? -impl BixFixedStrings { - pub fn to_bytes(self, dst_slice: &mut [u8]) -> Result { - if dst_slice.len() < BIX_OEM_INFO_END_IDX { - return Err(MessageSerializationError::BufferTooSmall); - } - - Ok(safe_put_dword(dst_slice, 0, self.revision)? - + safe_put_dword(dst_slice, 4, self.power_unit.into())? - + safe_put_dword(dst_slice, 8, self.design_capacity)? - + safe_put_dword(dst_slice, 12, self.last_full_charge_capacity)? - + safe_put_dword(dst_slice, 16, self.battery_technology.into())? - + safe_put_dword(dst_slice, 20, self.design_voltage)? - + safe_put_dword(dst_slice, 24, self.design_cap_of_warning)? - + safe_put_dword(dst_slice, 28, self.design_cap_of_low)? - + safe_put_dword(dst_slice, 32, self.cycle_count)? - + safe_put_dword(dst_slice, 36, self.measurement_accuracy)? - + safe_put_dword(dst_slice, 40, self.max_sampling_time)? - + safe_put_dword(dst_slice, 44, self.min_sampling_time)? - + safe_put_dword(dst_slice, 48, self.max_averaging_interval)? - + safe_put_dword(dst_slice, 52, self.min_averaging_interval)? - + safe_put_dword(dst_slice, 56, self.battery_capacity_granularity_1)? - + safe_put_dword(dst_slice, 60, self.battery_capacity_granularity_2)? - + safe_put_bytes(dst_slice, BIX_MODEL_NUM_START_IDX, &self.model_number)? - + safe_put_bytes(dst_slice, BIX_SERIAL_NUM_START_IDX, &self.serial_number)? - + safe_put_bytes(dst_slice, BIX_BATTERY_TYPE_START_IDX, &self.battery_type)? - + safe_put_bytes(dst_slice, BIX_OEM_INFO_START_IDX, &self.oem_info)? - + safe_put_dword(dst_slice, BIX_OEM_INFO_END_IDX, self.battery_swapping_capability.into())?) - } - - pub fn from_bytes(src_slice: &[u8]) -> Result { - Ok(Self { - revision: safe_get_dword(src_slice, 0)?, - power_unit: power_unit_try_from_u32(safe_get_dword(src_slice, 4)?)?, - design_capacity: safe_get_dword(src_slice, 8)?, - last_full_charge_capacity: safe_get_dword(src_slice, 12)?, - battery_technology: bat_tech_try_from_u32(safe_get_dword(src_slice, 16)?)?, - design_voltage: safe_get_dword(src_slice, 20)?, - design_cap_of_warning: safe_get_dword(src_slice, 24)?, - design_cap_of_low: safe_get_dword(src_slice, 28)?, - cycle_count: safe_get_dword(src_slice, 32)?, - measurement_accuracy: safe_get_dword(src_slice, 36)?, - max_sampling_time: safe_get_dword(src_slice, 40)?, - min_sampling_time: safe_get_dword(src_slice, 44)?, - max_averaging_interval: safe_get_dword(src_slice, 48)?, - min_averaging_interval: safe_get_dword(src_slice, 52)?, - battery_capacity_granularity_1: safe_get_dword(src_slice, 56)?, - battery_capacity_granularity_2: safe_get_dword(src_slice, 60)?, - model_number: safe_get_bytes::(src_slice, BIX_MODEL_NUM_START_IDX)?, - serial_number: safe_get_bytes::(src_slice, BIX_SERIAL_NUM_START_IDX)?, - battery_type: safe_get_bytes::(src_slice, BIX_BATTERY_TYPE_START_IDX)?, - oem_info: safe_get_bytes::(src_slice, BIX_OEM_INFO_START_IDX)?, - battery_swapping_capability: bat_swap_try_from_u32(safe_get_dword(src_slice, BIX_OEM_INFO_END_IDX)?)?, - }) - } -} - -const PIF_MODEL_NUM_START_IDX: usize = 12; -const PIF_MODEL_NUM_END_IDX: usize = PIF_MODEL_NUM_START_IDX + STD_BIX_MODEL_SIZE; -const PIF_SERIAL_NUM_START_IDX: usize = PIF_MODEL_NUM_END_IDX; -const PIF_SERIAL_NUM_END_IDX: usize = PIF_SERIAL_NUM_START_IDX + STD_BIX_SERIAL_SIZE; -const PIF_OEM_INFO_START_IDX: usize = PIF_SERIAL_NUM_END_IDX; -const PIF_OEM_INFO_END_IDX: usize = PIF_OEM_INFO_START_IDX + STD_BIX_OEM_SIZE; - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct PifFixedStrings { - /// Bitfield describing the state and characteristics of the power source. - pub power_source_state: PowerSourceState, - /// Maximum rated output power in milliwatts (mW). - /// - /// 0xFFFFFFFF indicates the value is unavailable. - pub max_output_power: u32, - /// Maximum rated input power in milliwatts (mW). - /// - /// 0xFFFFFFFF indicates the value is unavailable. - pub max_input_power: u32, - /// OEM-specific model number (ASCIIZ). Empty string if not supported. - pub model_number: [u8; STD_BIX_MODEL_SIZE], - /// OEM-specific serial number (ASCIIZ). Empty string if not supported. - pub serial_number: [u8; STD_BIX_SERIAL_SIZE], - /// OEM-specific information (ASCIIZ). Empty string if not supported. - pub oem_info: [u8; STD_BIX_OEM_SIZE], -} - -impl PifFixedStrings { - pub fn to_bytes(self, dst_slice: &mut [u8]) -> Result { - if dst_slice.len() < PIF_OEM_INFO_END_IDX { - return Err(MessageSerializationError::BufferTooSmall); - } - - Ok(safe_put_dword(dst_slice, 0, self.power_source_state.bits())? - + safe_put_dword(dst_slice, 4, self.max_output_power)? - + safe_put_dword(dst_slice, 8, self.max_input_power)? - + safe_put_bytes(dst_slice, PIF_MODEL_NUM_START_IDX, &self.model_number)? - + safe_put_bytes(dst_slice, PIF_SERIAL_NUM_START_IDX, &self.serial_number)? - + safe_put_bytes(dst_slice, PIF_OEM_INFO_START_IDX, &self.oem_info)?) - } - - pub fn from_bytes(src_slice: &[u8]) -> Result { - Ok(Self { - power_source_state: PowerSourceState::from_bits(safe_get_dword(src_slice, 0)?) - .ok_or(MessageSerializationError::InvalidPayload("Invalid PowerSourceState"))?, - max_output_power: safe_get_dword(src_slice, 4)?, - max_input_power: safe_get_dword(src_slice, 8)?, - model_number: safe_get_bytes::(src_slice, PIF_MODEL_NUM_START_IDX)?, - serial_number: safe_get_bytes::(src_slice, PIF_SERIAL_NUM_START_IDX)?, - oem_info: safe_get_bytes::(src_slice, PIF_OEM_INFO_START_IDX)?, - }) - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AcpiBatteryRequest { - BatteryGetBixRequest { battery_id: u8 }, - BatteryGetBstRequest { battery_id: u8 }, - BatteryGetPsrRequest { battery_id: u8 }, - BatteryGetPifRequest { battery_id: u8 }, - BatteryGetBpsRequest { battery_id: u8 }, - BatterySetBtpRequest { battery_id: u8, btp: Btp }, - BatterySetBptRequest { battery_id: u8, bpt: Bpt }, - BatteryGetBpcRequest { battery_id: u8 }, - BatterySetBmcRequest { battery_id: u8, bmc: Bmc }, - BatteryGetBmdRequest { battery_id: u8 }, - BatteryGetBctRequest { battery_id: u8, bct: Bct }, - BatteryGetBtmRequest { battery_id: u8, btm: Btm }, - BatterySetBmsRequest { battery_id: u8, bms: Bms }, - BatterySetBmaRequest { battery_id: u8, bma: Bma }, - BatteryGetStaRequest { battery_id: u8 }, -} - -impl SerializableMessage for AcpiBatteryRequest { - fn serialize(self, buffer: &mut [u8]) -> Result { - match self { - Self::BatteryGetBixRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatteryGetBstRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatteryGetPsrRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatteryGetPifRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatteryGetBpsRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatterySetBtpRequest { battery_id, btp } => { - Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, btp.trip_point)?) - } - Self::BatterySetBptRequest { battery_id, bpt } => Ok(safe_put_u8(buffer, 0, battery_id)? - + safe_put_dword(buffer, 1, bpt.revision)? - + safe_put_dword(buffer, 5, bpt.threshold_id as u32)? - + safe_put_dword(buffer, 9, bpt.threshold_value)?), - Self::BatteryGetBpcRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatterySetBmcRequest { battery_id, bmc } => { - Ok(safe_put_u8(buffer, 0, battery_id)? - + safe_put_dword(buffer, 1, bmc.maintenance_control_flags.bits())?) - } - Self::BatteryGetBmdRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - Self::BatteryGetBctRequest { battery_id, bct } => { - Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bct.charge_level_percent)?) - } - Self::BatteryGetBtmRequest { battery_id, btm } => { - Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, btm.discharge_rate)?) - } - Self::BatterySetBmsRequest { battery_id, bms } => { - Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bms.sampling_time_ms)?) - } - Self::BatterySetBmaRequest { battery_id, bma } => { - Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bma.averaging_interval_ms)?) - } - Self::BatteryGetStaRequest { battery_id } => safe_put_u8(buffer, 0, battery_id), - } - } - - fn deserialize(discriminant: u16, buffer: &[u8]) -> Result { - Ok( - match BatteryCmd::try_from(discriminant) - .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant))? - { - BatteryCmd::GetBix => Self::BatteryGetBixRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::GetBst => Self::BatteryGetBstRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::GetPsr => Self::BatteryGetPsrRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::GetPif => Self::BatteryGetPifRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::GetBps => Self::BatteryGetBpsRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::SetBtp => Self::BatterySetBtpRequest { - battery_id: safe_get_u8(buffer, 0)?, - btp: Btp { - trip_point: safe_get_dword(buffer, 1)?, - }, - }, - BatteryCmd::SetBpt => Self::BatterySetBptRequest { - battery_id: safe_get_u8(buffer, 0)?, - bpt: Bpt { - revision: safe_get_dword(buffer, 1)?, - threshold_id: thres_id_try_from_u32(safe_get_dword(buffer, 5)?)?, - threshold_value: safe_get_dword(buffer, 9)?, - }, - }, - BatteryCmd::GetBpc => Self::BatteryGetBpcRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::SetBmc => Self::BatterySetBmcRequest { - battery_id: safe_get_u8(buffer, 0)?, - bmc: Bmc { - maintenance_control_flags: BmcControlFlags::from_bits_retain(safe_get_dword(buffer, 1)?), - }, - }, - BatteryCmd::GetBmd => Self::BatteryGetBmdRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - BatteryCmd::GetBct => Self::BatteryGetBctRequest { - battery_id: safe_get_u8(buffer, 0)?, - bct: Bct { - charge_level_percent: safe_get_dword(buffer, 1)?, - }, - }, - BatteryCmd::GetBtm => Self::BatteryGetBtmRequest { - battery_id: safe_get_u8(buffer, 0)?, - btm: Btm { - discharge_rate: safe_get_dword(buffer, 1)?, - }, - }, - BatteryCmd::SetBms => Self::BatterySetBmsRequest { - battery_id: safe_get_u8(buffer, 0)?, - bms: Bms { - sampling_time_ms: safe_get_dword(buffer, 1)?, - }, - }, - BatteryCmd::SetBma => Self::BatterySetBmaRequest { - battery_id: safe_get_u8(buffer, 0)?, - bma: Bma { - averaging_interval_ms: safe_get_dword(buffer, 1)?, - }, - }, - BatteryCmd::GetSta => Self::BatteryGetStaRequest { - battery_id: safe_get_u8(buffer, 0)?, - }, - }, - ) - } - - fn discriminant(&self) -> u16 { - BatteryCmd::from(self).into() - } -} - -#[derive(PartialEq, Clone, Copy)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub enum AcpiBatteryResponse { - BatteryGetBixResponse { bix: BixFixedStrings }, - BatteryGetBstResponse { bst: BstReturn }, - BatteryGetPsrResponse { psr: PsrReturn }, - BatteryGetPifResponse { pif: PifFixedStrings }, - BatteryGetBpsResponse { bps: Bps }, - BatterySetBtpResponse {}, - BatterySetBptResponse {}, - BatteryGetBpcResponse { bpc: Bpc }, - BatterySetBmcResponse {}, - BatteryGetBmdResponse { bmd: Bmd }, - BatteryGetBctResponse { bct_response: BctReturnResult }, - BatteryGetBtmResponse { btm_response: BtmReturnResult }, - BatterySetBmsResponse { status: u32 }, - BatterySetBmaResponse { status: u32 }, - BatteryGetStaResponse { sta: StaReturn }, -} - -impl SerializableMessage for AcpiBatteryResponse { - fn serialize(self, buffer: &mut [u8]) -> Result { - match self { - Self::BatteryGetBixResponse { bix } => bix.to_bytes(buffer), - Self::BatteryGetBstResponse { bst } => Ok(safe_put_dword(buffer, 0, bst.battery_state.bits())? - + safe_put_dword(buffer, 4, bst.battery_present_rate)? - + safe_put_dword(buffer, 8, bst.battery_remaining_capacity)? - + safe_put_dword(buffer, 12, bst.battery_present_voltage)?), - Self::BatteryGetPsrResponse { psr } => safe_put_dword(buffer, 0, psr.power_source.into()), - - Self::BatteryGetPifResponse { pif } => pif.to_bytes(buffer), - Self::BatteryGetBpsResponse { bps } => Ok(safe_put_dword(buffer, 0, bps.revision)? - + safe_put_dword(buffer, 4, bps.instantaneous_peak_power_level)? - + safe_put_dword(buffer, 8, bps.instantaneous_peak_power_period)? - + safe_put_dword(buffer, 12, bps.sustainable_peak_power_level)? - + safe_put_dword(buffer, 16, bps.sustainable_peak_power_period)?), - Self::BatterySetBtpResponse {} => Ok(0), - Self::BatterySetBptResponse {} => Ok(0), - Self::BatteryGetBpcResponse { bpc } => Ok(safe_put_dword(buffer, 0, bpc.revision)? - + safe_put_dword(buffer, 4, bpc.power_threshold_support.bits())? - + safe_put_dword(buffer, 8, bpc.max_instantaneous_peak_power_threshold)? - + safe_put_dword(buffer, 12, bpc.max_sustainable_peak_power_threshold)?), - Self::BatterySetBmcResponse {} => Ok(0), - Self::BatteryGetBmdResponse { bmd } => Ok(safe_put_dword(buffer, 0, bmd.status_flags.bits())? - + safe_put_dword(buffer, 4, bmd.capability_flags.bits())? - + safe_put_dword(buffer, 8, bmd.recalibrate_count)? - + safe_put_dword(buffer, 12, bmd.quick_recalibrate_time)? - + safe_put_dword(buffer, 16, bmd.slow_recalibrate_time)?), - Self::BatteryGetBctResponse { bct_response } => safe_put_dword(buffer, 0, bct_response.into()), - Self::BatteryGetBtmResponse { btm_response } => safe_put_dword(buffer, 0, btm_response.into()), - Self::BatterySetBmsResponse { status } => safe_put_dword(buffer, 0, status), - Self::BatterySetBmaResponse { status } => safe_put_dword(buffer, 0, status), - Self::BatteryGetStaResponse { sta } => safe_put_dword(buffer, 0, sta.bits()), - } - } - - fn deserialize(discriminant: u16, buffer: &[u8]) -> Result { - Ok( - match BatteryCmd::try_from(discriminant) - .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant))? - { - BatteryCmd::GetBix => Self::BatteryGetBixResponse { - bix: BixFixedStrings::from_bytes(buffer)?, - }, - BatteryCmd::GetBst => { - let bst = BstReturn { - battery_state: BatteryState::from_bits(safe_get_dword(buffer, 0)?) - .ok_or(MessageSerializationError::BufferTooSmall)?, - battery_present_rate: safe_get_dword(buffer, 4)?, - battery_remaining_capacity: safe_get_dword(buffer, 8)?, - battery_present_voltage: safe_get_dword(buffer, 12)?, - }; - Self::BatteryGetBstResponse { bst } - } - BatteryCmd::GetPsr => Self::BatteryGetPsrResponse { - psr: PsrReturn { - power_source: pwr_src_try_from_u32(safe_get_dword(buffer, 0)?)?, - }, - }, - BatteryCmd::GetPif => Self::BatteryGetPifResponse { - pif: PifFixedStrings::from_bytes(buffer)?, - }, - BatteryCmd::GetBps => Self::BatteryGetBpsResponse { - bps: Bps { - revision: safe_get_dword(buffer, 0)?, - instantaneous_peak_power_level: safe_get_dword(buffer, 4)?, - instantaneous_peak_power_period: safe_get_dword(buffer, 8)?, - sustainable_peak_power_level: safe_get_dword(buffer, 12)?, - sustainable_peak_power_period: safe_get_dword(buffer, 16)?, - }, - }, - BatteryCmd::SetBtp => Self::BatterySetBtpResponse {}, - BatteryCmd::SetBpt => Self::BatterySetBptResponse {}, - BatteryCmd::GetBpc => Self::BatteryGetBpcResponse { - bpc: Bpc { - revision: safe_get_dword(buffer, 0)?, - power_threshold_support: PowerThresholdSupport::from_bits(safe_get_dword(buffer, 4)?) - .ok_or(MessageSerializationError::InvalidPayload("Invalid BpcThresholdSupport"))?, - max_instantaneous_peak_power_threshold: safe_get_dword(buffer, 8)?, - max_sustainable_peak_power_threshold: safe_get_dword(buffer, 12)?, - }, - }, - BatteryCmd::SetBmc => Self::BatterySetBmcResponse {}, - BatteryCmd::GetBmd => Self::BatteryGetBmdResponse { - bmd: Bmd { - status_flags: BmdStatusFlags::from_bits(safe_get_dword(buffer, 0)?) - .ok_or(MessageSerializationError::InvalidPayload("Invalid BmdStatusFlags"))?, - capability_flags: BmdCapabilityFlags::from_bits(safe_get_dword(buffer, 4)?) - .ok_or(MessageSerializationError::InvalidPayload("Invalid BmdCapabilityFlags"))?, - recalibrate_count: safe_get_dword(buffer, 8)?, - quick_recalibrate_time: safe_get_dword(buffer, 12)?, - slow_recalibrate_time: safe_get_dword(buffer, 16)?, - }, - }, - BatteryCmd::GetBct => Self::BatteryGetBctResponse { - bct_response: safe_get_dword(buffer, 0)?.into(), - }, - BatteryCmd::GetBtm => Self::BatteryGetBtmResponse { - btm_response: safe_get_dword(buffer, 0)?.into(), - }, - BatteryCmd::SetBms => Self::BatterySetBmsResponse { - status: safe_get_dword(buffer, 0)?, - }, - BatteryCmd::SetBma => Self::BatterySetBmaResponse { - status: safe_get_dword(buffer, 0)?, - }, - BatteryCmd::GetSta => Self::BatteryGetStaResponse { - sta: StaReturn::from_bits(safe_get_dword(buffer, 0)?) - .ok_or(MessageSerializationError::InvalidPayload("Invalid STA flags"))?, - }, - }, - ) - } - - fn discriminant(&self) -> u16 { - BatteryCmd::from(self).into() - } -} - -/// Fuel gauge ID -#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -pub struct DeviceId(pub u8); - -#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Copy, Clone, Debug, PartialEq)] -#[cfg_attr(feature = "defmt", derive(defmt::Format))] -#[repr(u16)] -pub enum AcpiBatteryError { - UnknownDeviceId = 1, - UnspecifiedFailure = 2, -} - -pub type AcpiBatteryResult = Result; - -impl SerializableMessage for AcpiBatteryError { - fn serialize(self, _buffer: &mut [u8]) -> Result { - match self { - AcpiBatteryError::UnknownDeviceId | AcpiBatteryError::UnspecifiedFailure => Ok(0), - } - } - - fn deserialize(discriminant: u16, _buffer: &[u8]) -> Result { - AcpiBatteryError::try_from(discriminant) - .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant)) - } - - fn discriminant(&self) -> u16 { - (*self).into() - } -} - -fn safe_get_u8(buffer: &[u8], index: usize) -> Result { - buffer - .get(index) - .copied() - .ok_or(MessageSerializationError::BufferTooSmall) -} - -fn safe_get_dword(buffer: &[u8], index: usize) -> Result { - let bytes = buffer - .get(index..index + 4) - .ok_or(MessageSerializationError::BufferTooSmall)? - .try_into() - .map_err(|_| MessageSerializationError::BufferTooSmall)?; - Ok(u32::from_le_bytes(bytes)) -} - -fn safe_get_bytes(buffer: &[u8], index: usize) -> Result<[u8; N], MessageSerializationError> { - buffer - .get(index..index + N) - .ok_or(MessageSerializationError::BufferTooSmall)? - .try_into() - .map_err(|_| MessageSerializationError::BufferTooSmall) -} - -fn safe_put_u8(buffer: &mut [u8], index: usize, val: u8) -> Result { - *buffer.get_mut(index).ok_or(MessageSerializationError::BufferTooSmall)? = val; - Ok(1) -} - -fn safe_put_dword(buffer: &mut [u8], index: usize, val: u32) -> Result { - buffer - .get_mut(index..index + 4) - .ok_or(MessageSerializationError::BufferTooSmall)? - .copy_from_slice(&val.to_le_bytes()); - Ok(4) -} - -fn safe_put_bytes(buffer: &mut [u8], index: usize, bytes: &[u8]) -> Result { - buffer - .get_mut(index..index + bytes.len()) - .ok_or(MessageSerializationError::BufferTooSmall)? - .copy_from_slice(bytes); - Ok(bytes.len()) -} diff --git a/battery-service-messages/Cargo.toml b/battery-service-relay/Cargo.toml similarity index 68% rename from battery-service-messages/Cargo.toml rename to battery-service-relay/Cargo.toml index e771e16f4..95d477c6f 100644 --- a/battery-service-messages/Cargo.toml +++ b/battery-service-relay/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "battery-service-messages" +name = "battery-service-relay" version.workspace = true edition.workspace = true license.workspace = true @@ -8,17 +8,19 @@ repository.workspace = true [dependencies] defmt = { workspace = true, optional = true } log = { workspace = true, optional = true } -embedded-batteries-async.workspace = true embedded-services.workspace = true num_enum.workspace = true -[lints] -workspace = true +battery-service-interface.workspace = true +embedded-batteries-async.workspace = true [features] defmt = [ "dep:defmt", "embedded-services/defmt", - "embedded-batteries-async/defmt", + "battery-service-interface/defmt", ] -log = ["dep:log", "embedded-services/log"] +log = ["dep:log", "embedded-services/log", "battery-service-interface/log"] + +[lints] +workspace = true diff --git a/battery-service-relay/src/lib.rs b/battery-service-relay/src/lib.rs new file mode 100644 index 000000000..92988ae2f --- /dev/null +++ b/battery-service-relay/src/lib.rs @@ -0,0 +1,100 @@ +#![no_std] + +use battery_service_interface::*; +use embedded_services::trace; + +mod serialization; +pub use serialization::{AcpiBatteryError, AcpiBatteryRequest, AcpiBatteryResponse, AcpiBatteryResult}; + +/// Relays messages to and from a battery service implementation over MCTP. +pub struct BatteryServiceRelayHandler { + service: S, +} + +impl BatteryServiceRelayHandler { + /// Create a new relay handler that uses the provided battery service implementation to handle requests. + pub fn new(service: S) -> Self { + Self { service } + } +} + +impl embedded_services::relay::mctp::RelayServiceHandlerTypes + for BatteryServiceRelayHandler +{ + type RequestType = serialization::AcpiBatteryRequest; + type ResultType = serialization::AcpiBatteryResult; +} + +impl embedded_services::relay::mctp::RelayServiceHandler + for BatteryServiceRelayHandler +{ + async fn process_request(&self, request: Self::RequestType) -> Self::ResultType { + trace!("Battery service: ACPI cmd recvd"); + Ok(match request { + AcpiBatteryRequest::GetBix { battery_id } => AcpiBatteryResponse::GetBix { + bix: self.service.battery_info(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::GetBst { battery_id } => AcpiBatteryResponse::GetBst { + bst: self.service.battery_status(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::GetPsr { battery_id } => AcpiBatteryResponse::GetPsr { + psr: self.service.is_in_use(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::GetPif { battery_id } => AcpiBatteryResponse::GetPif { + pif: self.service.power_source_information(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::GetBps { battery_id } => AcpiBatteryResponse::GetBps { + bps: self.service.battery_power_state(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::SetBtp { battery_id, btp } => { + self.service.set_battery_trip_point(DeviceId(battery_id), btp).await?; + AcpiBatteryResponse::SetBtp {} + } + AcpiBatteryRequest::SetBpt { battery_id, bpt } => { + self.service + .set_battery_power_threshold(DeviceId(battery_id), bpt) + .await?; + AcpiBatteryResponse::SetBpt {} + } + + AcpiBatteryRequest::GetBpc { battery_id } => AcpiBatteryResponse::GetBpc { + bpc: self.service.battery_power_characteristics(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::SetBmc { battery_id, bmc } => { + self.service + .battery_maintenance_control(DeviceId(battery_id), bmc) + .await?; + AcpiBatteryResponse::SetBmc {} + } + AcpiBatteryRequest::GetBmd { battery_id } => AcpiBatteryResponse::GetBmd { + bmd: self.service.battery_maintenance_data(DeviceId(battery_id)).await?, + }, + AcpiBatteryRequest::GetBct { battery_id, bct } => AcpiBatteryResponse::GetBct { + bct_response: self.service.battery_charge_time(DeviceId(battery_id), bct).await?, + }, + AcpiBatteryRequest::GetBtm { battery_id, btm } => AcpiBatteryResponse::GetBtm { + btm_response: self.service.battery_time_to_empty(DeviceId(battery_id), btm).await?, + }, + + AcpiBatteryRequest::SetBms { battery_id, bms } => { + self.service + .set_battery_measurement_sampling_time(DeviceId(battery_id), bms) + .await?; + AcpiBatteryResponse::SetBms { + status: 0, // TODO once we have a working reference platform, we should consider dropping this field, since it's redundant with the error type on Result. + } + } + AcpiBatteryRequest::SetBma { battery_id, bma } => { + self.service + .set_battery_measurement_averaging_interval(DeviceId(battery_id), bma) + .await?; + AcpiBatteryResponse::SetBma { + status: 0, // TODO once we have a working reference platform, we should consider dropping this field, since it's redundant with the error type on Result. + } + } + AcpiBatteryRequest::GetSta { battery_id } => AcpiBatteryResponse::GetSta { + sta: self.service.device_status(DeviceId(battery_id)).await?, + }, + }) + } +} diff --git a/battery-service-relay/src/serialization.rs b/battery-service-relay/src/serialization.rs new file mode 100644 index 000000000..39f411a44 --- /dev/null +++ b/battery-service-relay/src/serialization.rs @@ -0,0 +1,611 @@ +use battery_service_interface::*; +use embedded_services::relay::{MessageSerializationError, SerializableMessage}; + +#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Copy, Clone, Debug, PartialEq)] +#[repr(u16)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// ACPI Battery Methods +enum BatteryCmd { + /// Battery Information eXtended + GetBix = 1, + /// Battery Status + GetBst = 2, + /// Power Source + GetPsr = 3, + /// Power source InFormation + GetPif = 4, + /// Battery Power State + GetBps = 5, + /// Battery Trip Point + SetBtp = 6, + /// Battery Power Threshold + SetBpt = 7, + /// Battery Power Characteristics + GetBpc = 8, + /// Battery Maintenance Control + SetBmc = 9, + /// Battery Maintenance Data + GetBmd = 10, + /// Battery Charge Time + GetBct = 11, + /// Battery Time + GetBtm = 12, + /// Battery Measurement Sampling Time + SetBms = 13, + /// Battery Measurement Averaging Interval + SetBma = 14, + /// Device Status + GetSta = 15, +} + +impl From<&AcpiBatteryRequest> for BatteryCmd { + fn from(request: &AcpiBatteryRequest) -> Self { + match request { + AcpiBatteryRequest::GetBix { .. } => BatteryCmd::GetBix, + AcpiBatteryRequest::GetBst { .. } => BatteryCmd::GetBst, + AcpiBatteryRequest::GetPsr { .. } => BatteryCmd::GetPsr, + AcpiBatteryRequest::GetPif { .. } => BatteryCmd::GetPif, + AcpiBatteryRequest::GetBps { .. } => BatteryCmd::GetBps, + AcpiBatteryRequest::SetBtp { .. } => BatteryCmd::SetBtp, + AcpiBatteryRequest::SetBpt { .. } => BatteryCmd::SetBpt, + AcpiBatteryRequest::GetBpc { .. } => BatteryCmd::GetBpc, + AcpiBatteryRequest::SetBmc { .. } => BatteryCmd::SetBmc, + AcpiBatteryRequest::GetBmd { .. } => BatteryCmd::GetBmd, + AcpiBatteryRequest::GetBct { .. } => BatteryCmd::GetBct, + AcpiBatteryRequest::GetBtm { .. } => BatteryCmd::GetBtm, + AcpiBatteryRequest::SetBms { .. } => BatteryCmd::SetBms, + AcpiBatteryRequest::SetBma { .. } => BatteryCmd::SetBma, + AcpiBatteryRequest::GetSta { .. } => BatteryCmd::GetSta, + } + } +} + +impl From<&AcpiBatteryResponse> for BatteryCmd { + fn from(response: &AcpiBatteryResponse) -> Self { + match response { + AcpiBatteryResponse::GetBix { .. } => BatteryCmd::GetBix, + AcpiBatteryResponse::GetBst { .. } => BatteryCmd::GetBst, + AcpiBatteryResponse::GetPsr { .. } => BatteryCmd::GetPsr, + AcpiBatteryResponse::GetPif { .. } => BatteryCmd::GetPif, + AcpiBatteryResponse::GetBps { .. } => BatteryCmd::GetBps, + AcpiBatteryResponse::SetBtp { .. } => BatteryCmd::SetBtp, + AcpiBatteryResponse::SetBpt { .. } => BatteryCmd::SetBpt, + AcpiBatteryResponse::GetBpc { .. } => BatteryCmd::GetBpc, + AcpiBatteryResponse::SetBmc { .. } => BatteryCmd::SetBmc, + AcpiBatteryResponse::GetBmd { .. } => BatteryCmd::GetBmd, + AcpiBatteryResponse::GetBct { .. } => BatteryCmd::GetBct, + AcpiBatteryResponse::GetBtm { .. } => BatteryCmd::GetBtm, + AcpiBatteryResponse::SetBms { .. } => BatteryCmd::SetBms, + AcpiBatteryResponse::SetBma { .. } => BatteryCmd::SetBma, + AcpiBatteryResponse::GetSta { .. } => BatteryCmd::GetSta, + } + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +/// ACPI battery device message responses as defined in ACPI spec version 6.4, section 10.2 +pub enum AcpiBatteryResponse { + /// Extended battery information. Analogous to the return value of the _BIX method. + GetBix { bix: BixFixedStrings }, + + /// Battery status. Analogous to the return value of the _BST method. + GetBst { bst: BstReturn }, + + /// Power source in use. Analogous to the return value of the _PSR method. + GetPsr { psr: PsrReturn }, + + /// Power source information. Analogous to the return value of the _PIF method. + GetPif { pif: PifFixedStrings }, + + /// Battery power state. Analogous to the return value of the _BPS method. + GetBps { bps: Bps }, + + /// Result of setting a battery trip point. Analogous to the _BTP method. Semantically equivalent to (). + SetBtp {}, + + /// Result of setting a battery power threshold. Analogous to the _BPT method. Semantically equivalent to (). + SetBpt {}, + + /// Battery power characteristics. Analogous to the return value of the _BPC method. + GetBpc { bpc: Bpc }, + + /// Result of performing a battery maintenance control operation. Analogous to the return value of the _BMC method. Semantically equivalent to (). + SetBmc {}, + + /// Battery maintenance data. Analogous to the return value of the _BMD method. + GetBmd { bmd: Bmd }, + + /// Battery charge time. Analogous to the return value of the _BCT method. + GetBct { bct_response: BctReturnResult }, + + /// Battery time to empty. Analogous to the return value of the _BTM method. + GetBtm { btm_response: BtmReturnResult }, + + /// Result of setting the battery measurement sampling time. Analogous to the _BMS method. + SetBms { status: u32 }, + + /// Result of setting the battery measurement averaging interval. Analogous to the _BMA method. + SetBma { status: u32 }, + + /// Battery device status. Analogous to the return value of the _STA method. + GetSta { sta: StaReturn }, +} + +impl SerializableMessage for AcpiBatteryResponse { + fn serialize(self, buffer: &mut [u8]) -> Result { + match self { + Self::GetBix { bix } => bix_to_bytes(bix, buffer), + Self::GetBst { bst } => Ok(safe_put_dword(buffer, 0, bst.battery_state.bits())? + + safe_put_dword(buffer, 4, bst.battery_present_rate)? + + safe_put_dword(buffer, 8, bst.battery_remaining_capacity)? + + safe_put_dword(buffer, 12, bst.battery_present_voltage)?), + Self::GetPsr { psr } => safe_put_dword(buffer, 0, psr.power_source.into()), + + Self::GetPif { pif } => pif_to_bytes(pif, buffer), + Self::GetBps { bps } => Ok(safe_put_dword(buffer, 0, bps.revision)? + + safe_put_dword(buffer, 4, bps.instantaneous_peak_power_level)? + + safe_put_dword(buffer, 8, bps.instantaneous_peak_power_period)? + + safe_put_dword(buffer, 12, bps.sustainable_peak_power_level)? + + safe_put_dword(buffer, 16, bps.sustainable_peak_power_period)?), + Self::SetBtp {} => Ok(0), + Self::SetBpt {} => Ok(0), + Self::GetBpc { bpc } => Ok(safe_put_dword(buffer, 0, bpc.revision)? + + safe_put_dword(buffer, 4, bpc.power_threshold_support.bits())? + + safe_put_dword(buffer, 8, bpc.max_instantaneous_peak_power_threshold)? + + safe_put_dword(buffer, 12, bpc.max_sustainable_peak_power_threshold)?), + Self::SetBmc {} => Ok(0), + Self::GetBmd { bmd } => Ok(safe_put_dword(buffer, 0, bmd.status_flags.bits())? + + safe_put_dword(buffer, 4, bmd.capability_flags.bits())? + + safe_put_dword(buffer, 8, bmd.recalibrate_count)? + + safe_put_dword(buffer, 12, bmd.quick_recalibrate_time)? + + safe_put_dword(buffer, 16, bmd.slow_recalibrate_time)?), + Self::GetBct { bct_response } => safe_put_dword(buffer, 0, bct_response.into()), + Self::GetBtm { btm_response } => safe_put_dword(buffer, 0, btm_response.into()), + Self::SetBms { status } => safe_put_dword(buffer, 0, status), + Self::SetBma { status } => safe_put_dword(buffer, 0, status), + Self::GetSta { sta } => safe_put_dword(buffer, 0, sta.bits()), + } + } + + fn deserialize(discriminant: u16, buffer: &[u8]) -> Result { + Ok( + match BatteryCmd::try_from(discriminant) + .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant))? + { + BatteryCmd::GetBix => Self::GetBix { + bix: bix_from_bytes(buffer)?, + }, + BatteryCmd::GetBst => { + let bst = BstReturn { + battery_state: BatteryState::from_bits(safe_get_dword(buffer, 0)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid BatteryState"))?, + battery_present_rate: safe_get_dword(buffer, 4)?, + battery_remaining_capacity: safe_get_dword(buffer, 8)?, + battery_present_voltage: safe_get_dword(buffer, 12)?, + }; + Self::GetBst { bst } + } + BatteryCmd::GetPsr => Self::GetPsr { + psr: PsrReturn { + power_source: safe_get_dword(buffer, 0)? + .try_into() + .map_err(|_| MessageSerializationError::InvalidPayload("Invalid PowerSource"))?, + }, + }, + BatteryCmd::GetPif => Self::GetPif { + pif: pif_from_bytes(buffer)?, + }, + BatteryCmd::GetBps => Self::GetBps { + bps: Bps { + revision: safe_get_dword(buffer, 0)?, + instantaneous_peak_power_level: safe_get_dword(buffer, 4)?, + instantaneous_peak_power_period: safe_get_dword(buffer, 8)?, + sustainable_peak_power_level: safe_get_dword(buffer, 12)?, + sustainable_peak_power_period: safe_get_dword(buffer, 16)?, + }, + }, + BatteryCmd::SetBtp => Self::SetBtp {}, + BatteryCmd::SetBpt => Self::SetBpt {}, + BatteryCmd::GetBpc => Self::GetBpc { + bpc: Bpc { + revision: safe_get_dword(buffer, 0)?, + power_threshold_support: PowerThresholdSupport::from_bits(safe_get_dword(buffer, 4)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid BpcThresholdSupport"))?, + max_instantaneous_peak_power_threshold: safe_get_dword(buffer, 8)?, + max_sustainable_peak_power_threshold: safe_get_dword(buffer, 12)?, + }, + }, + BatteryCmd::SetBmc => Self::SetBmc {}, + BatteryCmd::GetBmd => Self::GetBmd { + bmd: Bmd { + status_flags: BmdStatusFlags::from_bits(safe_get_dword(buffer, 0)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid BmdStatusFlags"))?, + capability_flags: BmdCapabilityFlags::from_bits(safe_get_dword(buffer, 4)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid BmdCapabilityFlags"))?, + recalibrate_count: safe_get_dword(buffer, 8)?, + quick_recalibrate_time: safe_get_dword(buffer, 12)?, + slow_recalibrate_time: safe_get_dword(buffer, 16)?, + }, + }, + BatteryCmd::GetBct => Self::GetBct { + bct_response: safe_get_dword(buffer, 0)?.into(), + }, + BatteryCmd::GetBtm => Self::GetBtm { + btm_response: safe_get_dword(buffer, 0)?.into(), + }, + BatteryCmd::SetBms => Self::SetBms { + status: safe_get_dword(buffer, 0)?, + }, + BatteryCmd::SetBma => Self::SetBma { + status: safe_get_dword(buffer, 0)?, + }, + BatteryCmd::GetSta => Self::GetSta { + sta: StaReturn::from_bits(safe_get_dword(buffer, 0)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid STA flags"))?, + }, + }, + ) + } + + fn discriminant(&self) -> u16 { + BatteryCmd::from(self).into() + } +} + +#[derive(PartialEq, Clone, Copy)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +pub enum AcpiBatteryRequest { + /// Queries extended battery information. Analogous to ACPI's _BIX method. + GetBix { battery_id: u8 }, + + /// Queries battery status. Analogous to ACPI's _BST method. + GetBst { battery_id: u8 }, + + /// Queries the current power source. Analogous to ACPI's _PSR method. + GetPsr { battery_id: u8 }, + + /// Queries information about the battery's power source. Analogous to ACPI's _PIF method. + GetPif { battery_id: u8 }, + + /// Queries information about the current power delivery capabilities of the battery. Analogous to ACPI's _BPS method. + GetBps { battery_id: u8 }, + + /// Sets a battery trip point. Analogous to ACPI's _BTP method. + SetBtp { battery_id: u8, btp: Btp }, + + /// Sets a battery power threshold. Analogous to ACPI's _BPT method. + SetBpt { battery_id: u8, bpt: Bpt }, + + /// Queries the current power characteristics of the battery. Analogous to ACPI's _BPC method. + GetBpc { battery_id: u8 }, + + /// Performs a battery maintenance control operation. Analogous to ACPI's _BMC method. + SetBmc { battery_id: u8, bmc: Bmc }, + + /// Queries battery maintenance data. Analogous to ACPI's _BMD method. + GetBmd { battery_id: u8 }, + + /// Queries the estimated time remaining to charge the battery to the specified level. Analogous to ACPI's _BCT method. + GetBct { battery_id: u8, bct: Bct }, + + /// Queries the estimated time remaining until the battery is discharged to the specified level. Analogous to ACPI's _BTM method. + GetBtm { battery_id: u8, btm: Btm }, + + /// Sets the sampling time of battery measurements in milliseconds. Analogous to ACPI's _BMS method. + SetBms { battery_id: u8, bms: Bms }, + + /// Sets the averaging interval of battery measurements in milliseconds. Analogous to ACPI's _BMA method. + SetBma { battery_id: u8, bma: Bma }, + + /// Queries the current status of the battery device. Analogous to ACPI's _STA method. + GetSta { battery_id: u8 }, +} + +impl SerializableMessage for AcpiBatteryRequest { + fn serialize(self, buffer: &mut [u8]) -> Result { + match self { + Self::GetBix { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::GetBst { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::GetPsr { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::GetPif { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::GetBps { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::SetBtp { battery_id, btp } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, btp.trip_point)?) + } + Self::SetBpt { battery_id, bpt } => Ok(safe_put_u8(buffer, 0, battery_id)? + + safe_put_dword(buffer, 1, bpt.revision)? + + safe_put_dword(buffer, 5, bpt.threshold_id as u32)? + + safe_put_dword(buffer, 9, bpt.threshold_value)?), + Self::GetBpc { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::SetBmc { battery_id, bmc } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + + safe_put_dword(buffer, 1, bmc.maintenance_control_flags.bits())?) + } + Self::GetBmd { battery_id } => safe_put_u8(buffer, 0, battery_id), + Self::GetBct { battery_id, bct } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bct.charge_level_percent)?) + } + Self::GetBtm { battery_id, btm } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, btm.discharge_rate)?) + } + Self::SetBms { battery_id, bms } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bms.sampling_time_ms)?) + } + Self::SetBma { battery_id, bma } => { + Ok(safe_put_u8(buffer, 0, battery_id)? + safe_put_dword(buffer, 1, bma.averaging_interval_ms)?) + } + Self::GetSta { battery_id } => safe_put_u8(buffer, 0, battery_id), + } + } + + fn deserialize(discriminant: u16, buffer: &[u8]) -> Result { + Ok( + match BatteryCmd::try_from(discriminant) + .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant))? + { + BatteryCmd::GetBix => Self::GetBix { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::GetBst => Self::GetBst { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::GetPsr => Self::GetPsr { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::GetPif => Self::GetPif { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::GetBps => Self::GetBps { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::SetBtp => Self::SetBtp { + battery_id: safe_get_u8(buffer, 0)?, + btp: Btp { + trip_point: safe_get_dword(buffer, 1)?, + }, + }, + BatteryCmd::SetBpt => Self::SetBpt { + battery_id: safe_get_u8(buffer, 0)?, + bpt: Bpt { + revision: safe_get_dword(buffer, 1)?, + threshold_id: safe_get_dword(buffer, 5)? + .try_into() + .map_err(|_| MessageSerializationError::InvalidPayload("Invalid ThresholdId"))?, + threshold_value: safe_get_dword(buffer, 9)?, + }, + }, + BatteryCmd::GetBpc => Self::GetBpc { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::SetBmc => Self::SetBmc { + battery_id: safe_get_u8(buffer, 0)?, + bmc: Bmc { + maintenance_control_flags: BmcControlFlags::from_bits_retain(safe_get_dword(buffer, 1)?), + }, + }, + BatteryCmd::GetBmd => Self::GetBmd { + battery_id: safe_get_u8(buffer, 0)?, + }, + BatteryCmd::GetBct => Self::GetBct { + battery_id: safe_get_u8(buffer, 0)?, + bct: Bct { + charge_level_percent: safe_get_dword(buffer, 1)?, + }, + }, + BatteryCmd::GetBtm => Self::GetBtm { + battery_id: safe_get_u8(buffer, 0)?, + btm: Btm { + discharge_rate: safe_get_dword(buffer, 1)?, + }, + }, + BatteryCmd::SetBms => Self::SetBms { + battery_id: safe_get_u8(buffer, 0)?, + bms: Bms { + sampling_time_ms: safe_get_dword(buffer, 1)?, + }, + }, + BatteryCmd::SetBma => Self::SetBma { + battery_id: safe_get_u8(buffer, 0)?, + bma: Bma { + averaging_interval_ms: safe_get_dword(buffer, 1)?, + }, + }, + BatteryCmd::GetSta => Self::GetSta { + battery_id: safe_get_u8(buffer, 0)?, + }, + }, + ) + } + + fn discriminant(&self) -> u16 { + BatteryCmd::from(self).into() + } +} + +/// Serializable result type for battery operations. +pub type AcpiBatteryResult = Result; + +#[derive(num_enum::IntoPrimitive, num_enum::TryFromPrimitive, Copy, Clone, Debug, PartialEq)] +#[cfg_attr(feature = "defmt", derive(defmt::Format))] +#[repr(u16)] +/// Errors that can occur while processing ACPI battery requests. +pub enum AcpiBatteryError { + /// The provided battery ID does not correspond to any known battery device. + UnknownDeviceId = 1, + + /// An unspecified error occurred while processing the request. + UnspecifiedFailure = 2, +} + +impl SerializableMessage for AcpiBatteryError { + fn serialize(self, _buffer: &mut [u8]) -> Result { + match self { + AcpiBatteryError::UnknownDeviceId | AcpiBatteryError::UnspecifiedFailure => Ok(0), + } + } + + fn deserialize(discriminant: u16, _buffer: &[u8]) -> Result { + AcpiBatteryError::try_from(discriminant) + .map_err(|_| MessageSerializationError::UnknownMessageDiscriminant(discriminant)) + } + + fn discriminant(&self) -> u16 { + (*self).into() + } +} + +impl From for AcpiBatteryError { + fn from(error: BatteryError) -> Self { + match error { + BatteryError::UnknownDeviceId => AcpiBatteryError::UnknownDeviceId, + BatteryError::UnspecifiedFailure => AcpiBatteryError::UnspecifiedFailure, + } + } +} + +fn safe_get_u8(buffer: &[u8], index: usize) -> Result { + buffer + .get(index) + .copied() + .ok_or(MessageSerializationError::BufferTooSmall) +} + +fn safe_get_dword(buffer: &[u8], index: usize) -> Result { + let bytes = buffer + .get(index..index + 4) + .ok_or(MessageSerializationError::BufferTooSmall)? + .try_into() + .map_err(|_| MessageSerializationError::BufferTooSmall)?; + Ok(u32::from_le_bytes(bytes)) +} + +fn safe_get_bytes(buffer: &[u8], index: usize) -> Result<[u8; N], MessageSerializationError> { + buffer + .get(index..index + N) + .ok_or(MessageSerializationError::BufferTooSmall)? + .try_into() + .map_err(|_| MessageSerializationError::BufferTooSmall) +} + +fn safe_put_u8(buffer: &mut [u8], index: usize, val: u8) -> Result { + *buffer.get_mut(index).ok_or(MessageSerializationError::BufferTooSmall)? = val; + Ok(1) +} + +fn safe_put_dword(buffer: &mut [u8], index: usize, val: u32) -> Result { + buffer + .get_mut(index..index + 4) + .ok_or(MessageSerializationError::BufferTooSmall)? + .copy_from_slice(&val.to_le_bytes()); + Ok(4) +} + +fn safe_put_bytes(buffer: &mut [u8], index: usize, bytes: &[u8]) -> Result { + buffer + .get_mut(index..index + bytes.len()) + .ok_or(MessageSerializationError::BufferTooSmall)? + .copy_from_slice(bytes); + Ok(bytes.len()) +} + +const BIX_MODEL_NUM_START_IDX: usize = 64; +const BIX_MODEL_NUM_END_IDX: usize = BIX_MODEL_NUM_START_IDX + STD_BIX_MODEL_SIZE; +const BIX_SERIAL_NUM_START_IDX: usize = BIX_MODEL_NUM_END_IDX; +const BIX_SERIAL_NUM_END_IDX: usize = BIX_SERIAL_NUM_START_IDX + STD_BIX_SERIAL_SIZE; +const BIX_BATTERY_TYPE_START_IDX: usize = BIX_SERIAL_NUM_END_IDX; +const BIX_BATTERY_TYPE_END_IDX: usize = BIX_BATTERY_TYPE_START_IDX + STD_BIX_BATTERY_SIZE; +const BIX_OEM_INFO_START_IDX: usize = BIX_BATTERY_TYPE_END_IDX; +const BIX_OEM_INFO_END_IDX: usize = BIX_OEM_INFO_START_IDX + STD_BIX_OEM_SIZE; + +fn bix_to_bytes(bix: BixFixedStrings, dst_slice: &mut [u8]) -> Result { + if dst_slice.len() < BIX_OEM_INFO_END_IDX + core::mem::size_of::() { + return Err(MessageSerializationError::BufferTooSmall); + } + + Ok(safe_put_dword(dst_slice, 0, bix.revision)? + + safe_put_dword(dst_slice, 4, bix.power_unit.into())? + + safe_put_dword(dst_slice, 8, bix.design_capacity)? + + safe_put_dword(dst_slice, 12, bix.last_full_charge_capacity)? + + safe_put_dword(dst_slice, 16, bix.battery_technology.into())? + + safe_put_dword(dst_slice, 20, bix.design_voltage)? + + safe_put_dword(dst_slice, 24, bix.design_cap_of_warning)? + + safe_put_dword(dst_slice, 28, bix.design_cap_of_low)? + + safe_put_dword(dst_slice, 32, bix.cycle_count)? + + safe_put_dword(dst_slice, 36, bix.measurement_accuracy)? + + safe_put_dword(dst_slice, 40, bix.max_sampling_time)? + + safe_put_dword(dst_slice, 44, bix.min_sampling_time)? + + safe_put_dword(dst_slice, 48, bix.max_averaging_interval)? + + safe_put_dword(dst_slice, 52, bix.min_averaging_interval)? + + safe_put_dword(dst_slice, 56, bix.battery_capacity_granularity_1)? + + safe_put_dword(dst_slice, 60, bix.battery_capacity_granularity_2)? + + safe_put_bytes(dst_slice, BIX_MODEL_NUM_START_IDX, &bix.model_number)? + + safe_put_bytes(dst_slice, BIX_SERIAL_NUM_START_IDX, &bix.serial_number)? + + safe_put_bytes(dst_slice, BIX_BATTERY_TYPE_START_IDX, &bix.battery_type)? + + safe_put_bytes(dst_slice, BIX_OEM_INFO_START_IDX, &bix.oem_info)? + + safe_put_dword(dst_slice, BIX_OEM_INFO_END_IDX, bix.battery_swapping_capability.into())?) +} + +fn bix_from_bytes(src_slice: &[u8]) -> Result { + Ok(BixFixedStrings { + revision: safe_get_dword(src_slice, 0)?, + power_unit: safe_get_dword(src_slice, 4)? + .try_into() + .map_err(|_| MessageSerializationError::InvalidPayload("Invalid PowerUnit"))?, + design_capacity: safe_get_dword(src_slice, 8)?, + last_full_charge_capacity: safe_get_dword(src_slice, 12)?, + battery_technology: safe_get_dword(src_slice, 16)? + .try_into() + .map_err(|_| MessageSerializationError::InvalidPayload("Invalid BatteryTechnology"))?, + design_voltage: safe_get_dword(src_slice, 20)?, + design_cap_of_warning: safe_get_dword(src_slice, 24)?, + design_cap_of_low: safe_get_dword(src_slice, 28)?, + cycle_count: safe_get_dword(src_slice, 32)?, + measurement_accuracy: safe_get_dword(src_slice, 36)?, + max_sampling_time: safe_get_dword(src_slice, 40)?, + min_sampling_time: safe_get_dword(src_slice, 44)?, + max_averaging_interval: safe_get_dword(src_slice, 48)?, + min_averaging_interval: safe_get_dword(src_slice, 52)?, + battery_capacity_granularity_1: safe_get_dword(src_slice, 56)?, + battery_capacity_granularity_2: safe_get_dword(src_slice, 60)?, + model_number: safe_get_bytes::(src_slice, BIX_MODEL_NUM_START_IDX)?, + serial_number: safe_get_bytes::(src_slice, BIX_SERIAL_NUM_START_IDX)?, + battery_type: safe_get_bytes::(src_slice, BIX_BATTERY_TYPE_START_IDX)?, + oem_info: safe_get_bytes::(src_slice, BIX_OEM_INFO_START_IDX)?, + battery_swapping_capability: safe_get_dword(src_slice, BIX_OEM_INFO_END_IDX)? + .try_into() + .map_err(|_| MessageSerializationError::InvalidPayload("Invalid BatterySwappingCapability"))?, + }) +} + +const PIF_MODEL_NUM_START_IDX: usize = 12; +const PIF_MODEL_NUM_END_IDX: usize = PIF_MODEL_NUM_START_IDX + STD_PIF_MODEL_SIZE; +const PIF_SERIAL_NUM_START_IDX: usize = PIF_MODEL_NUM_END_IDX; +const PIF_SERIAL_NUM_END_IDX: usize = PIF_SERIAL_NUM_START_IDX + STD_PIF_SERIAL_SIZE; +const PIF_OEM_INFO_START_IDX: usize = PIF_SERIAL_NUM_END_IDX; +const PIF_OEM_INFO_END_IDX: usize = PIF_OEM_INFO_START_IDX + STD_PIF_OEM_SIZE; + +fn pif_to_bytes(pif: PifFixedStrings, dst_slice: &mut [u8]) -> Result { + if dst_slice.len() < PIF_OEM_INFO_END_IDX { + return Err(MessageSerializationError::BufferTooSmall); + } + + Ok(safe_put_dword(dst_slice, 0, pif.power_source_state.bits())? + + safe_put_dword(dst_slice, 4, pif.max_output_power)? + + safe_put_dword(dst_slice, 8, pif.max_input_power)? + + safe_put_bytes(dst_slice, PIF_MODEL_NUM_START_IDX, &pif.model_number)? + + safe_put_bytes(dst_slice, PIF_SERIAL_NUM_START_IDX, &pif.serial_number)? + + safe_put_bytes(dst_slice, PIF_OEM_INFO_START_IDX, &pif.oem_info)?) +} + +fn pif_from_bytes(src_slice: &[u8]) -> Result { + Ok(PifFixedStrings { + power_source_state: PowerSourceState::from_bits(safe_get_dword(src_slice, 0)?) + .ok_or(MessageSerializationError::InvalidPayload("Invalid PowerSourceState"))?, + max_output_power: safe_get_dword(src_slice, 4)?, + max_input_power: safe_get_dword(src_slice, 8)?, + model_number: safe_get_bytes::(src_slice, PIF_MODEL_NUM_START_IDX)?, + serial_number: safe_get_bytes::(src_slice, PIF_SERIAL_NUM_START_IDX)?, + oem_info: safe_get_bytes::(src_slice, PIF_OEM_INFO_START_IDX)?, + }) +} diff --git a/battery-service/Cargo.toml b/battery-service/Cargo.toml index 0483e8162..316594a87 100644 --- a/battery-service/Cargo.toml +++ b/battery-service/Cargo.toml @@ -12,7 +12,7 @@ workspace = true [dependencies] defmt = { workspace = true, optional = true } -battery-service-messages.workspace = true +battery-service-interface.workspace = true embassy-futures.workspace = true embassy-sync.workspace = true embassy-time.workspace = true @@ -22,25 +22,22 @@ embedded-hal.workspace = true embedded-services.workspace = true log = { workspace = true, optional = true } odp-service-common.workspace = true -zerocopy.workspace = true -mctp-rs = { workspace = true, features = ["espi"] } -heapless.workspace = true power-policy-interface.workspace = true [features] default = [] defmt = [ "dep:defmt", - "battery-service-messages/defmt", + "battery-service-interface/defmt", "embedded-services/defmt", "embassy-time/defmt", "embassy-sync/defmt", "embedded-batteries-async/defmt", - "mctp-rs/defmt", "power-policy-interface/defmt", ] log = [ "dep:log", + "battery-service-interface/log", "embedded-services/log", "embassy-time/log", "embassy-sync/log", diff --git a/battery-service/src/acpi.rs b/battery-service/src/acpi.rs index f0656a7db..52d0074de 100644 --- a/battery-service/src/acpi.rs +++ b/battery-service/src/acpi.rs @@ -1,18 +1,19 @@ #![allow(dead_code)] use core::ops::Deref; +use battery_service_interface::BatteryError; use embedded_batteries_async::acpi::{PowerSourceState, PowerUnit}; use embedded_services::{info, trace}; -use battery_service_messages::{ - AcpiBatteryResponse, BixFixedStrings, DeviceId, PifFixedStrings, STD_BIX_BATTERY_SIZE, STD_BIX_MODEL_SIZE, - STD_BIX_OEM_SIZE, STD_BIX_SERIAL_SIZE, STD_PIF_MODEL_SIZE, STD_PIF_OEM_SIZE, STD_PIF_SERIAL_SIZE, +use battery_service_interface::{ + BctReturnResult, BixFixedStrings, Bmd, Bpc, Bps, BstReturn, BtmReturnResult, DeviceId, PifFixedStrings, PsrReturn, + STD_BIX_BATTERY_SIZE, STD_BIX_MODEL_SIZE, STD_BIX_OEM_SIZE, STD_BIX_SERIAL_SIZE, STD_PIF_MODEL_SIZE, + STD_PIF_OEM_SIZE, STD_PIF_SERIAL_SIZE, StaReturn, }; use power_policy_interface::capability::PowerCapability; use crate::{ - AcpiBatteryError, context::PsuState, device::{DynamicBatteryMsgs, StaticBatteryMsgs}, }; @@ -181,222 +182,173 @@ pub(crate) fn compute_pif(psu_state: &PsuState) -> PifFixedStrings { } impl crate::context::Context { - // TODO Move these to a trait - pub(super) async fn bix_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn bix_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got BIX command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; let static_cache_guard = fg.get_static_battery_cache_guarded().await; let dynamic_cache_guard = fg.get_dynamic_battery_cache_guarded().await; - Ok(AcpiBatteryResponse::BatteryGetBixResponse { - bix: compute_bix(static_cache_guard.deref(), dynamic_cache_guard.deref()) - .map_err(|_| AcpiBatteryError::UnspecifiedFailure)?, - }) + compute_bix(static_cache_guard.deref(), dynamic_cache_guard.deref()) + .map_err(|_| BatteryError::UnspecifiedFailure) } - pub(super) async fn bst_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn bst_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got BST command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetBstResponse { - bst: compute_bst(&fg.get_dynamic_battery_cache().await), - }) + Ok(compute_bst(&fg.get_dynamic_battery_cache().await)) } - pub(super) async fn psr_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn psr_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got PSR command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetPsrResponse { - psr: compute_psr(&self.get_power_info().await), - }) + Ok(compute_psr(&self.get_power_info().await)) } - pub(super) async fn pif_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn pif_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got PIF command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetPifResponse { - pif: compute_pif(&self.get_power_info().await), - }) + Ok(compute_pif(&self.get_power_info().await)) } - pub(super) async fn bps_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn bps_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got BPS command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetBpsResponse { - bps: compute_bps(&fg.get_dynamic_battery_cache().await), - }) + Ok(compute_bps(&fg.get_dynamic_battery_cache().await)) } pub(super) async fn btp_handler( &self, device_id: DeviceId, btp: embedded_batteries_async::acpi::Btp, - ) -> Result { + ) -> Result<(), BatteryError> { trace!("Battery service: got BTP command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; // TODO: Save trip point info!("Battery service: New BTP {}", btp.trip_point); - Ok(AcpiBatteryResponse::BatterySetBtpResponse {}) + Ok(()) } pub(super) async fn bpt_handler( &self, device_id: DeviceId, bpt: embedded_batteries_async::acpi::Bpt, - ) -> Result { + ) -> Result<(), BatteryError> { trace!("Battery service: got BPT command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!( "Battery service: Threshold ID: {:?}, Threshold value: {:?}", bpt.threshold_id as u32, bpt.threshold_value ); - Ok(AcpiBatteryResponse::BatterySetBptResponse {}) + Ok(()) } - pub(super) async fn bpc_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn bpc_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got BPC command!"); // TODO: Save trip point - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetBpcResponse { - bpc: compute_bpc(&fg.get_static_battery_cache().await), - }) + Ok(compute_bpc(&fg.get_static_battery_cache().await)) } pub(super) async fn bmc_handler( &self, device_id: DeviceId, bmc: embedded_batteries_async::acpi::Bmc, - ) -> Result { + ) -> Result<(), BatteryError> { trace!("Battery service: got BMC command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!("Battery service: Bmc {}", bmc.maintenance_control_flags.bits()); - Ok(AcpiBatteryResponse::BatterySetBmcResponse {}) + Ok(()) } - pub(super) async fn bmd_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn bmd_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got BMD command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; let static_cache = fg.get_static_battery_cache().await; let dynamic_cache = fg.get_dynamic_battery_cache().await; - Ok(AcpiBatteryResponse::BatteryGetBmdResponse { - bmd: compute_bmd(&static_cache, &dynamic_cache), - }) + Ok(compute_bmd(&static_cache, &dynamic_cache)) } pub(super) async fn bct_handler( &self, device_id: DeviceId, bct: embedded_batteries_async::acpi::Bct, - ) -> Result { + ) -> Result { trace!("Battery service: got BCT command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!("Recvd BCT charge_level_percent: {}", bct.charge_level_percent); - Ok(AcpiBatteryResponse::BatteryGetBctResponse { - bct_response: compute_bct(&bct, &fg.get_dynamic_battery_cache().await), - }) + Ok(compute_bct(&bct, &fg.get_dynamic_battery_cache().await)) } pub(super) async fn btm_handler( &self, device_id: DeviceId, btm: embedded_batteries_async::acpi::Btm, - ) -> Result { + ) -> Result { trace!("Battery service: got BTM command!"); - let fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!("Recvd BTM discharge_rate: {}", btm.discharge_rate); - Ok(AcpiBatteryResponse::BatteryGetBtmResponse { - btm_response: compute_btm(&btm, &fg.get_dynamic_battery_cache().await), - }) + Ok(compute_btm(&btm, &fg.get_dynamic_battery_cache().await)) } pub(super) async fn bms_handler( &self, device_id: DeviceId, bms: embedded_batteries_async::acpi::Bms, - ) -> Result { + ) -> Result<(), BatteryError> { trace!("Battery service: got BMS command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!("Recvd BMS sampling_time: {}", bms.sampling_time_ms); - Ok(AcpiBatteryResponse::BatterySetBmsResponse { status: 0 }) + Ok(()) } pub(super) async fn bma_handler( &self, device_id: DeviceId, bma: embedded_batteries_async::acpi::Bma, - ) -> Result { + ) -> Result<(), BatteryError> { trace!("Battery service: got BMA command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; info!("Recvd BMA averaging_interval_ms: {}", bma.averaging_interval_ms); - Ok(AcpiBatteryResponse::BatterySetBmaResponse { status: 0 }) + Ok(()) } - pub(super) async fn sta_handler(&self, device_id: DeviceId) -> Result { + pub(super) async fn sta_handler(&self, device_id: DeviceId) -> Result { trace!("Battery service: got STA command!"); - let _fg = self - .get_fuel_gauge(device_id) - .ok_or(AcpiBatteryError::UnknownDeviceId)?; + let _fg = self.get_fuel_gauge(device_id).ok_or(BatteryError::UnknownDeviceId)?; - Ok(AcpiBatteryResponse::BatteryGetStaResponse { sta: compute_sta() }) + Ok(compute_sta()) } } diff --git a/battery-service/src/context.rs b/battery-service/src/context.rs index aaaed99b2..39c5648ed 100644 --- a/battery-service/src/context.rs +++ b/battery-service/src/context.rs @@ -1,7 +1,5 @@ -use crate::AcpiBatteryError; -use crate::device::{self}; -use crate::device::{Device, FuelGaugeError}; -use battery_service_messages::{AcpiBatteryRequest, AcpiBatteryResponse, DeviceId}; +use crate::device::{self, Device, FuelGaugeError}; +use battery_service_interface::DeviceId; use embassy_sync::channel::Channel; use embassy_sync::channel::TrySendError; use embassy_sync::mutex::Mutex; @@ -398,44 +396,6 @@ impl Context { } } - pub(super) async fn process_acpi_cmd( - &self, - acpi_msg: &AcpiBatteryRequest, - ) -> Result { - match *acpi_msg { - AcpiBatteryRequest::BatteryGetBixRequest { battery_id } => self.bix_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatteryGetBstRequest { battery_id } => self.bst_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatteryGetPsrRequest { battery_id } => self.psr_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatteryGetPifRequest { battery_id } => self.pif_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatteryGetBpsRequest { battery_id } => self.bps_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatterySetBtpRequest { battery_id, btp } => { - self.btp_handler(DeviceId(battery_id), btp).await - } - AcpiBatteryRequest::BatterySetBptRequest { battery_id, bpt } => { - self.bpt_handler(DeviceId(battery_id), bpt).await - } - AcpiBatteryRequest::BatteryGetBpcRequest { battery_id } => self.bpc_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatterySetBmcRequest { battery_id, bmc } => { - self.bmc_handler(DeviceId(battery_id), bmc).await - } - AcpiBatteryRequest::BatteryGetBmdRequest { battery_id } => self.bmd_handler(DeviceId(battery_id)).await, - AcpiBatteryRequest::BatteryGetBctRequest { battery_id, bct } => { - self.bct_handler(DeviceId(battery_id), bct).await - } - AcpiBatteryRequest::BatteryGetBtmRequest { battery_id, btm } => { - self.btm_handler(DeviceId(battery_id), btm).await - } - - AcpiBatteryRequest::BatterySetBmsRequest { battery_id, bms } => { - self.bms_handler(DeviceId(battery_id), bms).await - } - AcpiBatteryRequest::BatterySetBmaRequest { battery_id, bma } => { - self.bma_handler(DeviceId(battery_id), bma).await - } - AcpiBatteryRequest::BatteryGetStaRequest { battery_id } => self.sta_handler(DeviceId(battery_id)).await, - } - } - pub(crate) fn get_fuel_gauge(&self, id: DeviceId) -> Option<&'static Device> { for device in &self.fuel_gauges { if let Some(data) = device.data::() { diff --git a/battery-service/src/device.rs b/battery-service/src/device.rs index 3c0f73785..e4a3f28c0 100644 --- a/battery-service/src/device.rs +++ b/battery-service/src/device.rs @@ -9,7 +9,7 @@ use embedded_batteries_async::{ }; use embedded_services::{GlobalRawMutex, Node, NodeContainer, SyncCell}; -pub use battery_service_messages::DeviceId; +pub use battery_service_interface::DeviceId; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] diff --git a/battery-service/src/lib.rs b/battery-service/src/lib.rs index c0fbcb3a8..8548dbb81 100644 --- a/battery-service/src/lib.rs +++ b/battery-service/src/lib.rs @@ -2,13 +2,17 @@ use core::{any::Any, convert::Infallible}; -use battery_service_messages::{AcpiBatteryError, AcpiBatteryRequest, AcpiBatteryResult}; use context::BatteryEvent; use embedded_services::{ comms::{self, EndpointID}, error, info, trace, }; +use battery_service_interface::{ + BatteryError, Bct, BctReturnResult, BixFixedStrings, Bma, Bmc, Bmd, Bms, Bpc, Bps, Bpt, BstReturn, Btm, + BtmReturnResult, Btp, DeviceId, PifFixedStrings, PsrReturn, StaReturn, +}; + mod acpi; pub mod context; pub mod controller; @@ -153,6 +157,91 @@ impl<'hw, const N: usize> Service<'hw, N> { } } +impl<'hw, const N: usize> battery_service_interface::BatteryService for Service<'hw, N> { + async fn battery_charge_time( + &self, + battery_id: DeviceId, + charge_level: Bct, + ) -> Result { + self.inner.context.bct_handler(battery_id, charge_level).await + } + + async fn battery_info(&self, battery_id: DeviceId) -> Result { + self.inner.context.bix_handler(battery_id).await + } + + async fn set_battery_measurement_averaging_interval( + &self, + battery_id: DeviceId, + bma: Bma, + ) -> Result<(), BatteryError> { + self.inner.context.bma_handler(battery_id, bma).await + } + + async fn battery_maintenance_control(&self, battery_id: DeviceId, bmc: Bmc) -> Result<(), BatteryError> { + self.inner.context.bmc_handler(battery_id, bmc).await + } + + async fn battery_maintenance_data(&self, battery_id: DeviceId) -> Result { + self.inner.context.bmd_handler(battery_id).await + } + + async fn set_battery_measurement_sampling_time( + &self, + battery_id: DeviceId, + battery_measurement_sampling: Bms, + ) -> Result<(), BatteryError> { + self.inner + .context + .bms_handler(battery_id, battery_measurement_sampling) + .await + } + + async fn battery_power_characteristics(&self, battery_id: DeviceId) -> Result { + self.inner.context.bpc_handler(battery_id).await + } + + async fn battery_power_state(&self, battery_id: DeviceId) -> Result { + self.inner.context.bps_handler(battery_id).await + } + + async fn set_battery_power_threshold( + &self, + battery_id: DeviceId, + power_threshold: Bpt, + ) -> Result<(), BatteryError> { + self.inner.context.bpt_handler(battery_id, power_threshold).await + } + + async fn battery_status(&self, battery_id: DeviceId) -> Result { + self.inner.context.bst_handler(battery_id).await + } + + async fn battery_time_to_empty( + &self, + battery_id: DeviceId, + battery_discharge_rate: Btm, + ) -> Result { + self.inner.context.btm_handler(battery_id, battery_discharge_rate).await + } + + async fn set_battery_trip_point(&self, battery_id: DeviceId, btp: Btp) -> Result<(), BatteryError> { + self.inner.context.btp_handler(battery_id, btp).await + } + + async fn is_in_use(&self, battery_id: DeviceId) -> Result { + self.inner.context.psr_handler(battery_id).await + } + + async fn power_source_information(&self, power_source_id: DeviceId) -> Result { + self.inner.context.pif_handler(power_source_id).await + } + + async fn device_status(&self, battery_id: DeviceId) -> Result { + self.inner.context.sta_handler(battery_id).await + } +} + /// Errors that can occur during battery service initialization. #[derive(Debug)] #[cfg_attr(feature = "defmt", derive(defmt::Format))] @@ -192,22 +281,6 @@ where } } -impl embedded_services::relay::mctp::RelayServiceHandlerTypes for Service<'_, N> { - type RequestType = AcpiBatteryRequest; - type ResultType = AcpiBatteryResult; -} - -impl embedded_services::relay::mctp::RelayServiceHandler for Service<'_, N> { - async fn process_request(&self, request: Self::RequestType) -> Self::ResultType { - trace!("Battery service: ACPI cmd recvd"); - let response = self.inner.context.process_acpi_cmd(&request).await; - if let Err(e) = response { - error!("Battery service command failed: {:?}", e) - } - response - } -} - impl comms::MailboxDelegate for ServiceInner { fn receive(&self, message: &comms::Message) -> Result<(), comms::MailboxDelegateError> { if let Some(event) = message.data.get::() { diff --git a/battery-service/src/mock.rs b/battery-service/src/mock.rs index 25ba29dea..d852ea65f 100644 --- a/battery-service/src/mock.rs +++ b/battery-service/src/mock.rs @@ -174,11 +174,11 @@ impl crate::controller::Controller for MockBatteryDriver { min_averaging_interval: Default::default(), cap_granularity_1: Default::default(), cap_granularity_2: Default::default(), - power_threshold_support: battery_service_messages::PowerThresholdSupport::empty(), + power_threshold_support: battery_service_interface::PowerThresholdSupport::empty(), max_instant_pwr_threshold: Default::default(), max_sus_pwr_threshold: Default::default(), - bmc_flags: battery_service_messages::BmcControlFlags::empty(), - bmd_capability: battery_service_messages::BmdCapabilityFlags::empty(), + bmc_flags: battery_service_interface::BmcControlFlags::empty(), + bmd_capability: battery_service_interface::BmdCapabilityFlags::empty(), bmd_recalibrate_count: Default::default(), bmd_quick_recalibrate_time: Default::default(), bmd_slow_recalibrate_time: Default::default(), diff --git a/examples/pico-de-gallo/Cargo.lock b/examples/pico-de-gallo/Cargo.lock index 46376776a..e32f34de6 100644 --- a/examples/pico-de-gallo/Cargo.lock +++ b/examples/pico-de-gallo/Cargo.lock @@ -170,7 +170,7 @@ dependencies = [ name = "battery-service" version = "0.1.0" dependencies = [ - "battery-service-messages", + "battery-service-interface", "embassy-futures", "embassy-sync", "embassy-time", @@ -178,21 +178,17 @@ dependencies = [ "embedded-hal 1.0.0", "embedded-hal-async", "embedded-services", - "heapless 0.8.0", "log", - "mctp-rs", "odp-service-common", "power-policy-interface", - "zerocopy", ] [[package]] -name = "battery-service-messages" +name = "battery-service-interface" version = "0.1.0" dependencies = [ "embedded-batteries-async", - "embedded-services", - "num_enum", + "log", ] [[package]] diff --git a/examples/rt633/Cargo.lock b/examples/rt633/Cargo.lock index aca2363b1..56cef1eb1 100644 --- a/examples/rt633/Cargo.lock +++ b/examples/rt633/Cargo.lock @@ -41,7 +41,7 @@ dependencies = [ name = "battery-service" version = "0.1.0" dependencies = [ - "battery-service-messages", + "battery-service-interface", "defmt 0.3.100", "embassy-futures", "embassy-sync", @@ -50,21 +50,16 @@ dependencies = [ "embedded-hal 1.0.0", "embedded-hal-async", "embedded-services", - "heapless", - "mctp-rs", "odp-service-common", "power-policy-interface", - "zerocopy", ] [[package]] -name = "battery-service-messages" +name = "battery-service-interface" version = "0.1.0" dependencies = [ "defmt 0.3.100", "embedded-batteries-async", - "embedded-services", - "num_enum", ] [[package]] diff --git a/examples/rt685s-evk/Cargo.lock b/examples/rt685s-evk/Cargo.lock index ee421b4f9..889bfe641 100644 --- a/examples/rt685s-evk/Cargo.lock +++ b/examples/rt685s-evk/Cargo.lock @@ -174,6 +174,17 @@ dependencies = [ "syn 2.0.106", ] +[[package]] +name = "bitfield-struct" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8769c4854c5ada2852ddf6fd09d15cf43d4c2aaeccb4de6432f5402f08a6003b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.106", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -548,7 +559,7 @@ dependencies = [ "itertools 0.11.0", "mimxrt600-fcb 0.2.2", "mimxrt633s-pac", - "mimxrt685s-pac 0.5.0", + "mimxrt685s-pac", "nb 1.1.0", "paste", "rand_core", @@ -612,7 +623,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31e14d288a59ef41f4e05468eae9b1c9fef6866977cea86d3f1a1ced295b6cab" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.10.1", "bitflags 2.9.4", "defmt 0.3.100", "embedded-hal 1.0.0", @@ -625,7 +636,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b8996d7168535579180a0eead82efaba718ebd598782f986bfd635458259df2" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.10.1", "bitflags 2.9.4", "embedded-hal 1.0.0", "zerocopy", @@ -633,11 +644,11 @@ dependencies = [ [[package]] name = "embedded-batteries-async" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6465f32edac01ccd4d55931896177b3a61a5286205c5269f30b91ce7dbf51b5e" +checksum = "a3bf0e4be67770cfc31f1cea8b73baf98c0baf2c57d6bd8c3a4c315acb1d8bd4" dependencies = [ - "bitfield-struct", + "bitfield-struct 0.12.1", "embedded-batteries 0.3.1", "embedded-hal 1.0.0", ] @@ -1096,18 +1107,6 @@ dependencies = [ "vcell", ] -[[package]] -name = "mimxrt685s-pac" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c0b80e5add9dc74500acbb1ca70248e237d242b77988631e41db40a225f3a40" -dependencies = [ - "cortex-m", - "cortex-m-rt", - "critical-section", - "vcell", -] - [[package]] name = "mimxrt685s-pac" version = "0.5.0" @@ -1415,7 +1414,7 @@ dependencies = [ "embedded-usb-pd", "futures", "mimxrt600-fcb 0.1.0", - "mimxrt685s-pac 0.4.0", + "mimxrt685s-pac", "odp-service-common", "panic-probe", "platform-service", diff --git a/examples/std/Cargo.lock b/examples/std/Cargo.lock index a4c0d09dd..218efb11c 100644 --- a/examples/std/Cargo.lock +++ b/examples/std/Cargo.lock @@ -155,7 +155,7 @@ dependencies = [ name = "battery-service" version = "0.1.0" dependencies = [ - "battery-service-messages", + "battery-service-interface", "embassy-futures", "embassy-sync", "embassy-time", @@ -163,21 +163,17 @@ dependencies = [ "embedded-hal 1.0.0", "embedded-hal-async", "embedded-services", - "heapless", "log", - "mctp-rs", "odp-service-common", "power-policy-interface", - "zerocopy", ] [[package]] -name = "battery-service-messages" +name = "battery-service-interface" version = "0.1.0" dependencies = [ "embedded-batteries-async", - "embedded-services", - "num_enum", + "log", ] [[package]]