use std::fs::read_to_string;
use std::path::PathBuf;
use acpi_tables::aml;
use acpi_tables::aml::Aml;
use anyhow::Context;
use base::warn;
use vm_control::DeviceId;
use vm_control::PlatformDeviceId;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::Suspendable;
pub const ACDC_VIRT_MMIO_SIZE: u64 = 0x10;
const ACDC_ACEX: u32 = 0;
const _ACDC_RESERVED2: u32 = 0x4;
const _ACDC_RESERVED3: u32 = 0x8;
const _ACDC_RESERVED4: u32 = 0xc;
const ACDC_ACEX_UNINIT: u32 = 0xff;
pub struct AcAdapter {
pub acex: u32,
mmio_base: u64,
pub gpe_nr: u32,
}
impl AcAdapter {
pub fn new(mmio_base: u64, gpe_nr: u32) -> Self {
AcAdapter {
acex: ACDC_ACEX_UNINIT,
mmio_base,
gpe_nr,
}
}
fn get_init_state(&mut self) {
let mut ac_status = None;
let mut host_sysfs = PathBuf::new();
host_sysfs.push("/sys/class/power_supply/");
for entry in host_sysfs
.read_dir()
.expect("read_dir call failed")
.flatten()
{
let hid_path = entry.path().join("device/hid");
if hid_path.exists() {
if read_to_string(hid_path.as_path())
.with_context(|| format!("failed to read {}", hid_path.display()))
.unwrap()
.contains("ACPI0003")
{
ac_status = Some(
read_to_string(entry.path().join("online"))
.unwrap()
.trim()
.parse::<u32>()
.unwrap(),
);
}
}
}
if ac_status.is_none() {
warn!("Couldn't get ACPI0003 AC adapter state");
}
self.acex = ac_status.unwrap_or(0);
}
}
impl BusDevice for AcAdapter {
fn device_id(&self) -> DeviceId {
PlatformDeviceId::AcAdapter.into()
}
fn debug_label(&self) -> String {
"AcAdapter".to_owned()
}
fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
if data.len() != std::mem::size_of::<u32>() {
warn!(
"{}: unsupported read length {}, only support 4bytes read",
self.debug_label(),
data.len()
);
return;
}
let val = match info.offset as u32 {
ACDC_ACEX => {
if self.acex == ACDC_ACEX_UNINIT {
self.get_init_state();
}
self.acex
}
_ => {
warn!("{}: unsupported read address {}", self.debug_label(), info);
return;
}
};
let val_arr = val.to_le_bytes();
data.copy_from_slice(&val_arr);
}
}
impl Aml for AcAdapter {
fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
aml::Device::new(
"ACDC".into(),
vec![
&aml::Name::new("_HID".into(), &"ACPI0003"),
&aml::OpRegion::new(
"VREG".into(),
aml::OpRegionSpace::SystemMemory,
&self.mmio_base,
&(16_u32),
),
&aml::Field::new(
"VREG".into(),
aml::FieldAccessType::DWord,
aml::FieldLockRule::Lock,
aml::FieldUpdateRule::Preserve,
vec![aml::FieldEntry::Named(*b"ACEX", 32)],
),
&aml::Method::new(
"_PSR".into(),
0,
false,
vec![&aml::Return::new(&aml::Name::new_field_name("ACEX"))],
),
&aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]),
],
)
.to_aml_bytes(bytes);
aml::Scope::new(
"_GPE".into(),
vec![&aml::Method::new(
format!("_E{:02X}", self.gpe_nr).as_str().into(),
0,
false,
vec![&aml::Notify::new(&aml::Path::new("ACDC"), &0x80u8)],
)],
)
.to_aml_bytes(bytes);
}
}
impl Suspendable for AcAdapter {}