Files
RedBear-OS/local/patches/base/P4-acpi-s3-sleep.patch
T
vasilito 764dadd78e feat: ACPI S3 suspend-to-RAM + battery status queries (18 patches)
P3-4: ACPI S3 sleep — suspend_to_ram() method reads _S3 AML,
writes PM1a_CNT with SLEEP_EN + SLP_TYPa. Gated to x86/x86_64.
Applies after shutdown hardening for correct context.

P3-6: ACPI battery — read_battery_status() queries _BST,
read_battery_info() queries _BIF. Returns capacity/voltage data.

18/18 patches. base + base-initfs build.
2026-05-03 15:44:33 +01:00

88 lines
4.0 KiB
Diff

--- a/drivers/acpid/src/acpi.rs 2026-05-03 15:42:05.770648512 +0100
+++ b/drivers/acpid/src/acpi.rs 2026-05-03 15:42:05.778560344 +0100
@@ -654,6 +654,84 @@
log::error!("ACPI shutdown not supported on this architecture");
}
}
+
+ /// Suspend-to-RAM (S3 sleep state)
+ /// See ACPI 6.1 spec for SLP_TYPa/SLP_TYPb encoding
+ pub fn suspend_to_ram(&self) {
+ log::info!("ACPI: attempting suspend-to-RAM (S3)");
+ let fadt = match self.fadt() {
+ Some(f) => f,
+ None => { log::error!("ACPI S3: missing FADT"); return; }
+ };
+ let pm1a = fadt.pm1a_control_block as u16;
+ if pm1a == 0 {
+ log::error!("ACPI S3: PM1a port is zero");
+ return;
+ }
+ let aml_symbols = self.aml_symbols.read();
+ let s3_name = match acpi::aml::namespace::AmlName::from_str("\\_S3") {
+ Ok(n) => n,
+ Err(e) => { log::error!("ACPI S3: \\_S3 name error: {:?}", e); return; }
+ };
+ let s3 = match &aml_symbols.aml_context {
+ Some(ctx) => match ctx.namespace.lock().get(s3_name) {
+ Ok(s) => s,
+ Err(e) => { log::error!("ACPI S3: \\_S3 not found: {:?}", e); return; }
+ },
+ None => { log::error!("ACPI S3: AML context missing"); return; }
+ };
+ let pkg = match s3.deref() {
+ acpi::aml::object::Object::Package(p) => p,
+ _ => { log::error!("ACPI S3: \\_S3 not a package"); return; }
+ };
+ let slp_typa = match pkg[0].deref() {
+ acpi::aml::object::Object::Integer(i) => *i as u16,
+ _ => { log::error!("ACPI S3: SLP_TYPa not integer"); return; }
+ };
+ let mut val = (1u16 << 13) | (slp_typa & 0x1FFF);
+ log::info!("ACPI S3: writing PM1a=0x{:04X} val=0x{:04X}", pm1a, val);
+ #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+ { Pio::<u16>::new(pm1a).write(val); }
+ }
+
+
+ /// Query ACPI battery via \\_SB_.BAT0._BST
+ /// Returns (remaining_capacity_mAh, present_voltage_mV) if available
+ pub fn read_battery_status(&self) -> Option<(u32, u32)> {
+ let aml_symbols = self.aml_symbols.read();
+ let ctx = aml_symbols.aml_context.as_ref()?;
+ let mut ns = ctx.namespace.lock();
+ let bst_name = acpi::aml::namespace::AmlName::from_str("\\_SB_.BAT0._BST").ok()?;
+ let bst = ns.get(bst_name).ok()?;
+ match bst.deref() {
+ acpi::aml::object::Object::Package(p) if p.len() >= 4 => {
+ let cap = match p[1].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None };
+ let volt = match p[2].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None };
+ Some((cap, volt))
+ }
+ _ => { log::warn!("ACPI: _BST not a valid battery package"); None }
+ }
+ }
+
+ /// Query ACPI battery info via \\_SB_.BAT0._BIF
+ /// Returns (design_capacity_mAh, last_full_capacity_mAh, design_voltage_mV) if available
+ pub fn read_battery_info(&self) -> Option<(u32, u32, u32)> {
+ let aml_symbols = self.aml_symbols.read();
+ let ctx = aml_symbols.aml_context.as_ref()?;
+ let mut ns = ctx.namespace.lock();
+ let bif_name = acpi::aml::namespace::AmlName::from_str("\\_SB_.BAT0._BIF").ok()?;
+ let bif = ns.get(bif_name).ok()?;
+ match bif.deref() {
+ acpi::aml::object::Object::Package(p) if p.len() >= 13 => {
+ let design = match p[1].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None };
+ let last = match p[2].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None };
+ let volt = match p[4].deref() { acpi::aml::object::Object::Integer(i) => *i as u32, _ => return None };
+ Some((design, last, volt))
+ }
+ _ => { log::warn!("ACPI: _BIF not a valid battery package"); None }
+ }
+ }
+
}
#[repr(C, packed)]