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 9 use std::fs::read_to_string; 10 use std::path::PathBuf; 11 12 use acpi_tables::aml; 13 use acpi_tables::aml::Aml; 14 use anyhow::Context; 15 use base::warn; 16 17 use crate::pci::CrosvmDeviceId; 18 use crate::BusAccessInfo; 19 use crate::BusDevice; 20 use crate::DeviceId; 21 use crate::Suspendable; 22 23 pub const ACDC_VIRT_MMIO_SIZE: u64 = 0x10; 24 25 /// ACDC Virt MMIO offset 26 const ACDC_ACEX: u32 = 0; 27 const _ACDC_RESERVED2: u32 = 0x4; 28 const _ACDC_RESERVED3: u32 = 0x8; 29 const _ACDC_RESERVED4: u32 = 0xc; 30 31 const ACDC_ACEX_UNINIT: u32 = 0xff; 32 33 pub struct AcAdapter { 34 pub acex: u32, 35 mmio_base: u64, 36 pub gpe_nr: u32, 37 } 38 39 impl AcAdapter { new(mmio_base: u64, gpe_nr: u32) -> Self40 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. get_init_state(&mut self)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 90 impl BusDevice for AcAdapter { device_id(&self) -> DeviceId91 fn device_id(&self) -> DeviceId { 92 CrosvmDeviceId::AcAdapter.into() 93 } debug_label(&self) -> String94 fn debug_label(&self) -> String { 95 "AcAdapter".to_owned() 96 } 97 read(&mut self, info: BusAccessInfo, data: &mut [u8])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 126 impl Aml for AcAdapter { to_aml_bytes(&self, bytes: &mut Vec<u8>)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 168 impl Suspendable for AcAdapter {} 169