1use std::fs::read_to_string;
10use std::path::PathBuf;
11
12use acpi_tables::aml;
13use acpi_tables::aml::Aml;
14use anyhow::Context;
15use base::warn;
16
17use crate::pci::CrosvmDeviceId;
18use crate::BusAccessInfo;
19use crate::BusDevice;
20use crate::DeviceId;
21use crate::Suspendable;
22
23pub const ACDC_VIRT_MMIO_SIZE: u64 = 0x10;
24
25const ACDC_ACEX: u32 = 0;
27const _ACDC_RESERVED2: u32 = 0x4;
28const _ACDC_RESERVED3: u32 = 0x8;
29const _ACDC_RESERVED4: u32 = 0xc;
30
31const ACDC_ACEX_UNINIT: u32 = 0xff;
32
33pub struct AcAdapter {
34 pub acex: u32,
35 mmio_base: u64,
36 pub gpe_nr: u32,
37}
38
39impl AcAdapter {
40 pub fn new(mmio_base: u64, gpe_nr: u32) -> Self {
41 AcAdapter {
42 acex: ACDC_ACEX_UNINIT,
43 mmio_base,
44 gpe_nr,
45 }
46 }
47
48 fn get_init_state(&mut self) {
54 let mut ac_status = None;
58 let mut host_sysfs = PathBuf::new();
59 host_sysfs.push("/sys/class/power_supply/");
60 for entry in host_sysfs
61 .read_dir()
62 .expect("read_dir call failed")
63 .flatten()
64 {
65 let hid_path = entry.path().join("device/hid");
66 if hid_path.exists() {
67 if read_to_string(hid_path.as_path())
68 .with_context(|| format!("failed to read {}", hid_path.display()))
69 .unwrap()
70 .contains("ACPI0003")
71 {
72 ac_status = Some(
73 read_to_string(entry.path().join("online"))
74 .unwrap()
75 .trim()
76 .parse::<u32>()
77 .unwrap(),
78 );
79 }
80 }
81 }
82
83 if ac_status.is_none() {
84 warn!("Couldn't get ACPI0003 AC adapter state");
85 }
86 self.acex = ac_status.unwrap_or(0);
87 }
88}
89
90impl BusDevice for AcAdapter {
91 fn device_id(&self) -> DeviceId {
92 CrosvmDeviceId::AcAdapter.into()
93 }
94 fn debug_label(&self) -> String {
95 "AcAdapter".to_owned()
96 }
97
98 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
99 if data.len() != std::mem::size_of::<u32>() {
100 warn!(
101 "{}: unsupported read length {}, only support 4bytes read",
102 self.debug_label(),
103 data.len()
104 );
105 return;
106 }
107
108 let val = match info.offset as u32 {
109 ACDC_ACEX => {
110 if self.acex == ACDC_ACEX_UNINIT {
111 self.get_init_state();
112 }
113 self.acex
114 }
115 _ => {
116 warn!("{}: unsupported read address {}", self.debug_label(), info);
117 return;
118 }
119 };
120
121 let val_arr = val.to_le_bytes();
122 data.copy_from_slice(&val_arr);
123 }
124}
125
126impl Aml for AcAdapter {
127 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
128 aml::Device::new(
129 "ACDC".into(),
130 vec![
131 &aml::Name::new("_HID".into(), &"ACPI0003"),
132 &aml::OpRegion::new(
133 "VREG".into(),
134 aml::OpRegionSpace::SystemMemory,
135 &self.mmio_base,
136 &(16_u32),
137 ),
138 &aml::Field::new(
139 "VREG".into(),
140 aml::FieldAccessType::DWord,
141 aml::FieldLockRule::Lock,
142 aml::FieldUpdateRule::Preserve,
143 vec![aml::FieldEntry::Named(*b"ACEX", 32)],
144 ),
145 &aml::Method::new(
146 "_PSR".into(),
147 0,
148 false,
149 vec![&aml::Return::new(&aml::Name::new_field_name("ACEX"))],
150 ),
151 &aml::Method::new("_STA".into(), 0, false, vec![&aml::Return::new(&0xfu8)]),
152 ],
153 )
154 .to_aml_bytes(bytes);
155 aml::Scope::new(
156 "_GPE".into(),
157 vec![&aml::Method::new(
158 format!("_E{:02X}", self.gpe_nr).as_str().into(),
159 0,
160 false,
161 vec![&aml::Notify::new(&aml::Path::new("ACDC"), &0x80u8)],
162 )],
163 )
164 .to_aml_bytes(bytes);
165 }
166}
167
168impl Suspendable for AcAdapter {}