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 {}