1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// Copyright 2023 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// The ACPI AC adapter device (ACPI0003) description can be found in ACPI specification,
// section: 10.3. The AC adapter status change is signalized by generating associated GPE, which
// Notify()'s AC adapter device in the ACPI name space.

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 crate::pci::CrosvmDeviceId;
use crate::BusAccessInfo;
use crate::BusDevice;
use crate::DeviceId;
use crate::Suspendable;

pub const ACDC_VIRT_MMIO_SIZE: u64 = 0x10;

/// ACDC Virt MMIO offset
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,
        }
    }

    // The AC adapter state update is triggered upon handling "ac_adapter" acpi event, nevertheless
    // the init value is retrieved from host's sysfs. Determining it from AcAdapter::new would be
    // racy since after AcAdapter creation but before acpi listener is activated any status change
    // will be lost. Determining init state in such way will be triggered only once during guest
    // driver probe.
    fn get_init_state(&mut self) {
        // Find host AC adapter (ACPI0003 HID) and read its state.
        // e.g. hid     /sys/class/power_supply/ADP1/device/hid
        //      state   /sys/class/power_supply/ADP1/online
        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 {
        CrosvmDeviceId::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 {}