• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 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 use std::sync::Arc;
6 
7 use acpi_tables::aml;
8 use acpi_tables::aml::Aml;
9 use base::error;
10 use base::warn;
11 use base::AsRawDescriptor;
12 use base::Event;
13 use base::EventToken;
14 use base::RawDescriptor;
15 use base::Tube;
16 use base::WaitContext;
17 use base::WorkerThread;
18 use power_monitor::BatteryStatus;
19 use power_monitor::CreatePowerMonitorFn;
20 use remain::sorted;
21 use sync::Mutex;
22 use thiserror::Error;
23 use vm_control::BatControlCommand;
24 use vm_control::BatControlResult;
25 
26 use crate::pci::CrosvmDeviceId;
27 use crate::BusAccessInfo;
28 use crate::BusDevice;
29 use crate::DeviceId;
30 use crate::IrqLevelEvent;
31 use crate::Suspendable;
32 
33 /// Errors for battery devices.
34 #[sorted]
35 #[derive(Error, Debug)]
36 pub enum BatteryError {
37     #[error("Non 32-bit mmio address space")]
38     Non32BitMmioAddress,
39 }
40 
41 type Result<T> = std::result::Result<T, BatteryError>;
42 
43 /// the GoldFish Battery MMIO length.
44 pub const GOLDFISHBAT_MMIO_LEN: u64 = 0x1000;
45 
46 struct GoldfishBatteryState {
47     // interrupt state
48     int_status: u32,
49     int_enable: u32,
50     // AC state
51     ac_online: u32,
52     // Battery state
53     status: u32,
54     health: u32,
55     present: u32,
56     capacity: u32,
57     voltage: u32,
58     current: u32,
59     charge_counter: u32,
60     charge_full: u32,
61 }
62 
63 macro_rules! create_battery_func {
64     // $property: the battery property which is going to be modified.
65     // $int: the interrupt status which is going to be set to notify the guest.
66     ($fn:ident, $property:ident, $int:ident) => {
67         pub(crate) fn $fn(&mut self, value: u32) -> bool {
68             let old = std::mem::replace(&mut self.$property, value);
69             old != self.$property && self.set_int_status($int)
70         }
71     };
72 }
73 
74 impl GoldfishBatteryState {
set_int_status(&mut self, mask: u32) -> bool75     fn set_int_status(&mut self, mask: u32) -> bool {
76         if ((self.int_enable & mask) != 0) && ((self.int_status & mask) == 0) {
77             self.int_status |= mask;
78             return true;
79         }
80         false
81     }
82 
int_status(&self) -> u3283     fn int_status(&self) -> u32 {
84         self.int_status
85     }
86 
87     create_battery_func!(set_ac_online, ac_online, AC_STATUS_CHANGED);
88 
89     create_battery_func!(set_status, status, BATTERY_STATUS_CHANGED);
90 
91     create_battery_func!(set_health, health, BATTERY_STATUS_CHANGED);
92 
93     create_battery_func!(set_present, present, BATTERY_STATUS_CHANGED);
94 
95     create_battery_func!(set_capacity, capacity, BATTERY_STATUS_CHANGED);
96 
97     create_battery_func!(set_voltage, voltage, BATTERY_STATUS_CHANGED);
98 
99     create_battery_func!(set_current, current, BATTERY_STATUS_CHANGED);
100 
101     create_battery_func!(set_charge_counter, charge_counter, BATTERY_STATUS_CHANGED);
102 
103     create_battery_func!(set_charge_full, charge_full, BATTERY_STATUS_CHANGED);
104 }
105 
106 /// GoldFish Battery state
107 pub struct GoldfishBattery {
108     state: Arc<Mutex<GoldfishBatteryState>>,
109     mmio_base: u32,
110     irq_num: u32,
111     irq_evt: IrqLevelEvent,
112     activated: bool,
113     monitor_thread: Option<WorkerThread<()>>,
114     tube: Option<Tube>,
115     create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
116 }
117 
118 /// Goldfish Battery MMIO offset
119 const BATTERY_INT_STATUS: u32 = 0;
120 const BATTERY_INT_ENABLE: u32 = 0x4;
121 const BATTERY_AC_ONLINE: u32 = 0x8;
122 const BATTERY_STATUS: u32 = 0xC;
123 const BATTERY_HEALTH: u32 = 0x10;
124 const BATTERY_PRESENT: u32 = 0x14;
125 const BATTERY_CAPACITY: u32 = 0x18;
126 const BATTERY_VOLTAGE: u32 = 0x1C;
127 const BATTERY_TEMP: u32 = 0x20;
128 const BATTERY_CHARGE_COUNTER: u32 = 0x24;
129 const BATTERY_VOLTAGE_MAX: u32 = 0x28;
130 const BATTERY_CURRENT_MAX: u32 = 0x2C;
131 const BATTERY_CURRENT_NOW: u32 = 0x30;
132 const BATTERY_CURRENT_AVG: u32 = 0x34;
133 const BATTERY_CHARGE_FULL_UAH: u32 = 0x38;
134 const BATTERY_CYCLE_COUNT: u32 = 0x40;
135 
136 /// Goldfish Battery interrupt bits
137 const BATTERY_STATUS_CHANGED: u32 = 1 << 0;
138 const AC_STATUS_CHANGED: u32 = 1 << 1;
139 const BATTERY_INT_MASK: u32 = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED;
140 
141 /// Goldfish Battery status
142 const BATTERY_STATUS_VAL_UNKNOWN: u32 = 0;
143 const BATTERY_STATUS_VAL_CHARGING: u32 = 1;
144 const BATTERY_STATUS_VAL_DISCHARGING: u32 = 2;
145 const BATTERY_STATUS_VAL_NOT_CHARGING: u32 = 3;
146 
147 /// Goldfish Battery health
148 const BATTERY_HEALTH_VAL_UNKNOWN: u32 = 0;
149 
150 #[derive(EventToken)]
151 pub(crate) enum Token {
152     Commands,
153     Resample,
154     Kill,
155     Monitor,
156 }
157 
command_monitor( tube: Tube, irq_evt: IrqLevelEvent, kill_evt: Event, state: Arc<Mutex<GoldfishBatteryState>>, create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>, )158 fn command_monitor(
159     tube: Tube,
160     irq_evt: IrqLevelEvent,
161     kill_evt: Event,
162     state: Arc<Mutex<GoldfishBatteryState>>,
163     create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
164 ) {
165     let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
166         (&tube, Token::Commands),
167         (irq_evt.get_resample(), Token::Resample),
168         (&kill_evt, Token::Kill),
169     ]) {
170         Ok(pc) => pc,
171         Err(e) => {
172             error!("failed to build WaitContext: {}", e);
173             return;
174         }
175     };
176 
177     let mut power_monitor = match create_power_monitor {
178         Some(f) => match f() {
179             Ok(p) => match wait_ctx.add(p.get_read_notifier(), Token::Monitor) {
180                 Ok(()) => Some(p),
181                 Err(e) => {
182                     error!("failed to add power monitor to poll context: {}", e);
183                     None
184                 }
185             },
186             Err(e) => {
187                 error!("failed to create power monitor: {}", e);
188                 None
189             }
190         },
191         None => None,
192     };
193 
194     'poll: loop {
195         let events = match wait_ctx.wait() {
196             Ok(v) => v,
197             Err(e) => {
198                 error!("error while polling for events: {}", e);
199                 break;
200             }
201         };
202 
203         for event in events.iter().filter(|e| e.is_readable) {
204             match event.token {
205                 Token::Commands => {
206                     let req = match tube.recv() {
207                         Ok(req) => req,
208                         Err(e) => {
209                             error!("failed to receive request: {}", e);
210                             continue;
211                         }
212                     };
213 
214                     let mut bat_state = state.lock();
215                     let inject_irq = match req {
216                         BatControlCommand::SetStatus(status) => bat_state.set_status(status.into()),
217                         BatControlCommand::SetHealth(health) => bat_state.set_health(health.into()),
218                         BatControlCommand::SetPresent(present) => {
219                             let v = present != 0;
220                             bat_state.set_present(v.into())
221                         }
222                         BatControlCommand::SetCapacity(capacity) => {
223                             let v = std::cmp::min(capacity, 100);
224                             bat_state.set_capacity(v)
225                         }
226                         BatControlCommand::SetACOnline(ac_online) => {
227                             let v = ac_online != 0;
228                             bat_state.set_ac_online(v.into())
229                         }
230                     };
231 
232                     if inject_irq {
233                         let _ = irq_evt.trigger();
234                     }
235 
236                     if let Err(e) = tube.send(&BatControlResult::Ok) {
237                         error!("failed to send response: {}", e);
238                     }
239                 }
240 
241                 Token::Monitor => {
242                     // Safe because power_monitor must be populated if Token::Monitor is triggered.
243                     let power_monitor = power_monitor.as_mut().unwrap();
244 
245                     let data = match power_monitor.read_message() {
246                         Ok(Some(d)) => d,
247                         Ok(None) => continue,
248                         Err(e) => {
249                             error!("failed to read new power data: {}", e);
250                             continue;
251                         }
252                     };
253 
254                     let mut bat_state = state.lock();
255 
256                     // Each set_* function called below returns true when interrupt bits
257                     // (*_STATUS_CHANGED) changed. If `inject_irq` is true after we attempt to
258                     // update each field, inject an interrupt.
259                     let mut inject_irq = bat_state.set_ac_online(data.ac_online.into());
260 
261                     match data.battery {
262                         Some(battery_data) => {
263                             inject_irq |= bat_state.set_capacity(battery_data.percent);
264                             let battery_status = match battery_data.status {
265                                 BatteryStatus::Unknown => BATTERY_STATUS_VAL_UNKNOWN,
266                                 BatteryStatus::Charging => BATTERY_STATUS_VAL_CHARGING,
267                                 BatteryStatus::Discharging => BATTERY_STATUS_VAL_DISCHARGING,
268                                 BatteryStatus::NotCharging => BATTERY_STATUS_VAL_NOT_CHARGING,
269                             };
270                             inject_irq |= bat_state.set_status(battery_status);
271                             inject_irq |= bat_state.set_voltage(battery_data.voltage);
272                             inject_irq |= bat_state.set_current(battery_data.current);
273                             inject_irq |= bat_state.set_charge_counter(battery_data.charge_counter);
274                             inject_irq |= bat_state.set_charge_full(battery_data.charge_full);
275                         }
276                         None => {
277                             inject_irq |= bat_state.set_present(0);
278                         }
279                     }
280 
281                     if inject_irq {
282                         let _ = irq_evt.trigger();
283                     }
284                 }
285 
286                 Token::Resample => {
287                     irq_evt.clear_resample();
288                     if state.lock().int_status() != 0 {
289                         let _ = irq_evt.trigger();
290                     }
291                 }
292 
293                 Token::Kill => break 'poll,
294             }
295         }
296     }
297 }
298 
299 impl GoldfishBattery {
300     /// Create GoldfishBattery device model
301     ///
302     /// * `mmio_base` - The 32-bit mmio base address.
303     /// * `irq_num` - The corresponding interrupt number of the irq_evt
304     ///               which will be put into the ACPI DSDT.
305     /// * `irq_evt` - The interrupt event used to notify driver about
306     ///               the battery properties changing.
307     /// * `socket` - Battery control socket
new( mmio_base: u64, irq_num: u32, irq_evt: IrqLevelEvent, tube: Tube, create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>, ) -> Result<Self>308     pub fn new(
309         mmio_base: u64,
310         irq_num: u32,
311         irq_evt: IrqLevelEvent,
312         tube: Tube,
313         create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
314     ) -> Result<Self> {
315         if mmio_base + GOLDFISHBAT_MMIO_LEN - 1 > u32::MAX as u64 {
316             return Err(BatteryError::Non32BitMmioAddress);
317         }
318         let state = Arc::new(Mutex::new(GoldfishBatteryState {
319             capacity: 50,
320             health: BATTERY_HEALTH_VAL_UNKNOWN,
321             present: 1,
322             status: BATTERY_STATUS_VAL_UNKNOWN,
323             ac_online: 1,
324             int_enable: 0,
325             int_status: 0,
326             voltage: 0,
327             current: 0,
328             charge_counter: 0,
329             charge_full: 0,
330         }));
331 
332         Ok(GoldfishBattery {
333             state,
334             mmio_base: mmio_base as u32,
335             irq_num,
336             irq_evt,
337             activated: false,
338             monitor_thread: None,
339             tube: Some(tube),
340             create_power_monitor,
341         })
342     }
343 
344     /// return the descriptors used by this device
keep_rds(&self) -> Vec<RawDescriptor>345     pub fn keep_rds(&self) -> Vec<RawDescriptor> {
346         let mut rds = vec![
347             self.irq_evt.get_trigger().as_raw_descriptor(),
348             self.irq_evt.get_resample().as_raw_descriptor(),
349         ];
350 
351         if let Some(tube) = &self.tube {
352             rds.push(tube.as_raw_descriptor());
353         }
354 
355         rds
356     }
357 
358     /// start a monitor thread to monitor the events from host
start_monitor(&mut self)359     fn start_monitor(&mut self) {
360         if self.activated {
361             return;
362         }
363 
364         if let Some(tube) = self.tube.take() {
365             let irq_evt = self.irq_evt.try_clone().unwrap();
366             let bat_state = self.state.clone();
367             let create_monitor_fn = self.create_power_monitor.take();
368             self.monitor_thread = Some(WorkerThread::start(self.debug_label(), move |kill_evt| {
369                 command_monitor(tube, irq_evt, kill_evt, bat_state, create_monitor_fn)
370             }));
371             self.activated = true;
372         }
373     }
374 }
375 
376 impl Drop for GoldfishBattery {
drop(&mut self)377     fn drop(&mut self) {
378         if let Err(e) = self.sleep() {
379             error!("{}", e);
380         };
381     }
382 }
383 
384 impl BusDevice for GoldfishBattery {
device_id(&self) -> DeviceId385     fn device_id(&self) -> DeviceId {
386         CrosvmDeviceId::GoldfishBattery.into()
387     }
388 
debug_label(&self) -> String389     fn debug_label(&self) -> String {
390         "GoldfishBattery".to_owned()
391     }
392 
read(&mut self, info: BusAccessInfo, data: &mut [u8])393     fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
394         if data.len() != std::mem::size_of::<u32>() {
395             warn!(
396                 "{}: unsupported read length {}, only support 4bytes read",
397                 self.debug_label(),
398                 data.len()
399             );
400             return;
401         }
402 
403         let val = match info.offset as u32 {
404             BATTERY_INT_STATUS => {
405                 // read to clear the interrupt status
406                 std::mem::replace(&mut self.state.lock().int_status, 0)
407             }
408             BATTERY_INT_ENABLE => self.state.lock().int_enable,
409             BATTERY_AC_ONLINE => self.state.lock().ac_online,
410             BATTERY_STATUS => self.state.lock().status,
411             BATTERY_HEALTH => self.state.lock().health,
412             BATTERY_PRESENT => self.state.lock().present,
413             BATTERY_CAPACITY => self.state.lock().capacity,
414             BATTERY_VOLTAGE => self.state.lock().voltage,
415             BATTERY_TEMP => 0,
416             BATTERY_CHARGE_COUNTER => self.state.lock().charge_counter,
417             BATTERY_VOLTAGE_MAX => 0,
418             BATTERY_CURRENT_MAX => 0,
419             BATTERY_CURRENT_NOW => self.state.lock().current,
420             BATTERY_CURRENT_AVG => 0,
421             BATTERY_CHARGE_FULL_UAH => self.state.lock().charge_full,
422             BATTERY_CYCLE_COUNT => 0,
423             _ => {
424                 warn!("{}: unsupported read address {}", self.debug_label(), info);
425                 return;
426             }
427         };
428 
429         let val_arr = val.to_ne_bytes();
430         data.copy_from_slice(&val_arr);
431     }
432 
write(&mut self, info: BusAccessInfo, data: &[u8])433     fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
434         if data.len() != std::mem::size_of::<u32>() {
435             warn!(
436                 "{}: unsupported write length {}, only support 4bytes write",
437                 self.debug_label(),
438                 data.len()
439             );
440             return;
441         }
442 
443         let mut val_arr = u32::to_ne_bytes(0u32);
444         val_arr.copy_from_slice(data);
445         let val = u32::from_ne_bytes(val_arr);
446 
447         match info.offset as u32 {
448             BATTERY_INT_ENABLE => {
449                 self.state.lock().int_enable = val;
450                 if (val & BATTERY_INT_MASK) != 0 && !self.activated {
451                     self.start_monitor();
452                 }
453             }
454             _ => {
455                 warn!("{}: Bad write to address {}", self.debug_label(), info);
456             }
457         };
458     }
459 }
460 
461 impl Aml for GoldfishBattery {
to_aml_bytes(&self, bytes: &mut Vec<u8>)462     fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
463         aml::Device::new(
464             "GFBY".into(),
465             vec![
466                 &aml::Name::new("_HID".into(), &"GFSH0001"),
467                 &aml::Name::new(
468                     "_CRS".into(),
469                     &aml::ResourceTemplate::new(vec![
470                         &aml::Memory32Fixed::new(true, self.mmio_base, GOLDFISHBAT_MMIO_LEN as u32),
471                         &aml::Interrupt::new(true, false, false, true, self.irq_num),
472                     ]),
473                 ),
474             ],
475         )
476         .to_aml_bytes(bytes);
477     }
478 }
479 
480 impl Suspendable for GoldfishBattery {
sleep(&mut self) -> anyhow::Result<()>481     fn sleep(&mut self) -> anyhow::Result<()> {
482         if let Some(thread) = self.monitor_thread.take() {
483             thread.stop();
484         }
485         Ok(())
486     }
487 
wake(&mut self) -> anyhow::Result<()>488     fn wake(&mut self) -> anyhow::Result<()> {
489         if self.activated {
490             // Set activated to false for start_monitor to start monitoring again.
491             self.activated = false;
492             self.start_monitor();
493         }
494         Ok(())
495     }
496 }
497