devices/
ac_adapter.rs

1// Copyright 2023 The ChromiumOS Authors
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5// The ACPI AC adapter device (ACPI0003) description can be found in ACPI specification,
6// section: 10.3. The AC adapter status change is signalized by generating associated GPE, which
7// Notify()'s AC adapter device in the ACPI name space.
8
9use 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
25/// ACDC Virt MMIO offset
26const 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    // The AC adapter state update is triggered upon handling "ac_adapter" acpi event, nevertheless
49    // the init value is retrieved from host's sysfs. Determining it from AcAdapter::new would be
50    // racy since after AcAdapter creation but before acpi listener is activated any status change
51    // will be lost. Determining init state in such way will be triggered only once during guest
52    // driver probe.
53    fn get_init_state(&mut self) {
54        // Find host AC adapter (ACPI0003 HID) and read its state.
55        // e.g. hid     /sys/class/power_supply/ADP1/device/hid
56        //      state   /sys/class/power_supply/ADP1/online
57        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 {}