1use std::ops::DerefMut;
6use std::sync::Arc;
7use std::time::Duration;
8use std::time::SystemTime;
9
10use acpi_tables::aml;
11use acpi_tables::aml::Aml;
12use anyhow::bail;
13use anyhow::Context;
14use base::error;
15use base::warn;
16use base::AsRawDescriptor;
17use base::Event;
18use base::EventToken;
19use base::RawDescriptor;
20use base::ReadNotifier;
21use base::Tube;
22use base::WaitContext;
23use base::WorkerThread;
24use power_monitor::BatteryStatus;
25use power_monitor::CreatePowerClientFn;
26use power_monitor::CreatePowerMonitorFn;
27use power_monitor::PowerClient;
28use remain::sorted;
29use serde::Deserialize;
30use serde::Serialize;
31use snapshot::AnySnapshot;
32use sync::Mutex;
33use thiserror::Error;
34use vm_control::BatControlCommand;
35use vm_control::BatControlResult;
36use vm_control::DeviceId;
37use vm_control::PlatformDeviceId;
38
39use crate::BusAccessInfo;
40use crate::BusDevice;
41use crate::IrqLevelEvent;
42use crate::Suspendable;
43
44#[sorted]
46#[derive(Error, Debug)]
47pub enum BatteryError {
48 #[error("Non 32-bit mmio address space")]
49 Non32BitMmioAddress,
50}
51
52type Result<T> = std::result::Result<T, BatteryError>;
53
54pub const GOLDFISHBAT_MMIO_LEN: u64 = 0x1000;
56
57#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Copy)]
59pub enum BatConfig {
60 #[default]
62 Real,
63 Fake {
67 max_capacity: u32,
69 },
70}
71
72#[derive(Clone, Serialize, Deserialize)]
73struct GoldfishBatteryState {
74 int_status: u32,
76 int_enable: u32,
77 ac_online: u32,
79 status: u32,
81 health: u32,
82 present: u32,
83 capacity: u32,
84 voltage: u32,
85 current: u32,
86 charge_counter: u32,
87 charge_full: u32,
88 bat_config: BatConfig,
90}
91
92macro_rules! create_battery_func {
93 ($fn:ident, $property:ident, $ty:ty, $int:ident) => {
97 pub(crate) fn $fn(&mut self, value: $ty) -> bool {
98 let old = std::mem::replace(&mut self.$property, value);
99 old != self.$property && self.set_int_status($int)
100 }
101 };
102}
103
104impl GoldfishBatteryState {
105 fn set_int_status(&mut self, mask: u32) -> bool {
106 let old = self.int_status;
107 self.int_status |= self.int_enable & mask;
108 old != self.int_status
109 }
110
111 fn int_status(&self) -> u32 {
112 self.int_status
113 }
114
115 create_battery_func!(set_ac_online, ac_online, u32, AC_STATUS_CHANGED);
116
117 create_battery_func!(set_status, status, u32, BATTERY_STATUS_CHANGED);
118
119 create_battery_func!(set_health, health, u32, BATTERY_STATUS_CHANGED);
120
121 create_battery_func!(set_present, present, u32, BATTERY_STATUS_CHANGED);
122
123 create_battery_func!(set_capacity, capacity, u32, BATTERY_STATUS_CHANGED);
124
125 create_battery_func!(set_voltage, voltage, u32, BATTERY_STATUS_CHANGED);
126
127 create_battery_func!(set_current, current, u32, BATTERY_STATUS_CHANGED);
128
129 create_battery_func!(
130 set_charge_counter,
131 charge_counter,
132 u32,
133 BATTERY_STATUS_CHANGED
134 );
135
136 create_battery_func!(set_charge_full, charge_full, u32, BATTERY_STATUS_CHANGED);
137
138 create_battery_func!(set_bat_config, bat_config, BatConfig, BATTERY_INT_MASK);
139
140 fn update_from_power_data(&mut self, data: power_monitor::PowerData) -> bool {
141 let mut inject_irq = self.set_ac_online(data.ac_online.into());
144
145 match data.battery {
146 Some(battery_data) => {
147 inject_irq |= self.set_present(1);
148 inject_irq |= self.set_capacity(battery_data.percent);
149 let battery_status = match battery_data.status {
150 BatteryStatus::Unknown => BATTERY_STATUS_VAL_UNKNOWN,
151 BatteryStatus::Charging => BATTERY_STATUS_VAL_CHARGING,
152 BatteryStatus::Discharging => BATTERY_STATUS_VAL_DISCHARGING,
153 BatteryStatus::NotCharging => BATTERY_STATUS_VAL_NOT_CHARGING,
154 };
155 inject_irq |= self.set_status(battery_status);
156 inject_irq |= self.set_voltage(battery_data.voltage);
157 inject_irq |= self.set_current(battery_data.current);
158 inject_irq |= self.set_charge_counter(battery_data.charge_counter);
159 inject_irq |= self.set_charge_full(battery_data.charge_full);
160 }
161 None => {
162 inject_irq |= self.set_present(0);
163 }
164 }
165 inject_irq
166 }
167}
168
169enum BatInitializationState {
170 NotYet,
171 Pending(Box<dyn PowerClient>),
172 Done,
173}
174
175pub struct GoldfishBattery {
177 state: Arc<Mutex<GoldfishBatteryState>>,
178 mmio_base: u32,
179 irq_num: u32,
180 irq_evt: IrqLevelEvent,
181 activated: bool,
182 monitor_thread: Option<WorkerThread<()>>,
183 tube: Option<Tube>,
184 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
185 create_powerd_client: Option<Box<dyn CreatePowerClientFn>>,
186 init_state: Arc<Mutex<BatInitializationState>>,
187}
188
189#[derive(Serialize, Deserialize)]
190struct GoldfishBatterySnapshot {
191 state: GoldfishBatteryState,
192 mmio_base: u32,
193 irq_num: u32,
194 activated: bool,
195}
196
197const BATTERY_INT_STATUS: u32 = 0;
199const BATTERY_INT_ENABLE: u32 = 0x4;
200const BATTERY_AC_ONLINE: u32 = 0x8;
201const BATTERY_STATUS: u32 = 0xC;
202const BATTERY_HEALTH: u32 = 0x10;
203const BATTERY_PRESENT: u32 = 0x14;
204const BATTERY_CAPACITY: u32 = 0x18;
205const BATTERY_VOLTAGE: u32 = 0x1C;
206const BATTERY_TEMP: u32 = 0x20;
207const BATTERY_CHARGE_COUNTER: u32 = 0x24;
208const BATTERY_VOLTAGE_MAX: u32 = 0x28;
209const BATTERY_CURRENT_MAX: u32 = 0x2C;
210const BATTERY_CURRENT_NOW: u32 = 0x30;
211const BATTERY_CURRENT_AVG: u32 = 0x34;
212const BATTERY_CHARGE_FULL_UAH: u32 = 0x38;
213const BATTERY_CYCLE_COUNT: u32 = 0x40;
214
215const BATTERY_STATUS_CHANGED: u32 = 1 << 0;
217const AC_STATUS_CHANGED: u32 = 1 << 1;
218const BATTERY_INT_MASK: u32 = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED;
219
220const BATTERY_STATUS_VAL_UNKNOWN: u32 = 0;
222const BATTERY_STATUS_VAL_CHARGING: u32 = 1;
223const BATTERY_STATUS_VAL_DISCHARGING: u32 = 2;
224const BATTERY_STATUS_VAL_NOT_CHARGING: u32 = 3;
225
226const BATTERY_HEALTH_VAL_UNKNOWN: u32 = 0;
228
229const AC_ONLINE_VAL_OFFLINE: u32 = 0;
231
232#[derive(EventToken)]
233pub(crate) enum Token {
234 Commands,
235 Resample,
236 Kill,
237 Monitor,
238}
239
240fn command_monitor(
241 tube: Tube,
242 irq_evt: IrqLevelEvent,
243 kill_evt: Event,
244 state: Arc<Mutex<GoldfishBatteryState>>,
245 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
246 init_state: Arc<Mutex<BatInitializationState>>,
247) {
248 let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
253 (tube.get_read_notifier(), Token::Commands),
254 (irq_evt.get_resample(), Token::Resample),
255 (&kill_evt, Token::Kill),
256 ]) {
257 Ok(pc) => pc,
258 Err(e) => {
259 error!("failed to build WaitContext: {}", e);
260 return;
261 }
262 };
263
264 let mut power_monitor = match create_power_monitor {
265 Some(f) => match f() {
266 Ok(p) => match wait_ctx.add(p.get_read_notifier(), Token::Monitor) {
267 Ok(()) => Some(p),
268 Err(e) => {
269 error!("failed to add power monitor to poll context: {}", e);
270 None
271 }
272 },
273 Err(e) => {
274 error!("failed to create power monitor: {}", e);
275 None
276 }
277 },
278 None => None,
279 };
280
281 'poll: loop {
282 let events = match wait_ctx.wait() {
283 Ok(v) => v,
284 Err(e) => {
285 error!("error while polling for events: {}", e);
286 break;
287 }
288 };
289
290 for event in events.iter().filter(|e| e.is_readable) {
291 match event.token {
292 Token::Commands => {
293 let req = match tube.recv() {
294 Ok(req) => req,
295 Err(e) => {
296 error!("failed to receive request: {}", e);
297 continue;
298 }
299 };
300
301 let mut bat_state = state.lock();
302 let inject_irq = match req {
303 BatControlCommand::SetStatus(status) => bat_state.set_status(status.into()),
304 BatControlCommand::SetHealth(health) => bat_state.set_health(health.into()),
305 BatControlCommand::SetPresent(present) => {
306 let v = present != 0;
307 bat_state.set_present(v.into())
308 }
309 BatControlCommand::SetCapacity(capacity) => {
310 let v = std::cmp::min(capacity, 100);
311 bat_state.set_capacity(v)
312 }
313 BatControlCommand::SetACOnline(ac_online) => {
314 let v = ac_online != 0;
315 bat_state.set_ac_online(v.into())
316 }
317 BatControlCommand::SetFakeBatConfig(max_capacity) => {
318 let max_capacity = std::cmp::min(max_capacity, 100);
319 bat_state.set_bat_config(BatConfig::Fake { max_capacity })
320 }
321 BatControlCommand::CancelFakeConfig => {
322 bat_state.set_bat_config(BatConfig::Real)
323 }
324 };
325
326 if inject_irq {
327 let _ = irq_evt.trigger();
328 }
329
330 if let Err(e) = tube.send(&BatControlResult::Ok) {
331 error!("failed to send response: {}", e);
332 }
333 }
334
335 Token::Monitor => {
336 let power_monitor = power_monitor.as_mut().unwrap();
338
339 let data = match power_monitor.read_message() {
340 Ok(Some(d)) => d,
341 Ok(None) => continue,
342 Err(e) => {
343 error!("failed to read new power data: {}", e);
344 continue;
345 }
346 };
347
348 let inject_irq = {
349 let mut init_state = init_state.lock();
353 let mut bat_state = state.lock();
354
355 let inject_irq = bat_state.update_from_power_data(data);
358 *init_state = BatInitializationState::Done;
359 inject_irq
360 };
361
362 if inject_irq {
363 let _ = irq_evt.trigger();
364 }
365 }
366
367 Token::Resample => {
368 irq_evt.clear_resample();
369 if state.lock().int_status() != 0 {
370 let _ = irq_evt.trigger();
371 }
372 }
373
374 Token::Kill => break 'poll,
375 }
376 }
377 }
378}
379
380impl GoldfishBattery {
381 pub(crate) const POWERD_REQ_INTERVAL_MS: u64 = 1000;
384
385 pub fn new(
394 mmio_base: u64,
395 irq_num: u32,
396 irq_evt: IrqLevelEvent,
397 tube: Tube,
398 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
399 create_powerd_client: Option<Box<dyn CreatePowerClientFn>>,
400 ) -> Result<Self> {
401 if mmio_base + GOLDFISHBAT_MMIO_LEN - 1 > u32::MAX as u64 {
402 return Err(BatteryError::Non32BitMmioAddress);
403 }
404 let state = Arc::new(Mutex::new(GoldfishBatteryState {
405 capacity: 50,
406 health: BATTERY_HEALTH_VAL_UNKNOWN,
407 present: 1,
408 status: BATTERY_STATUS_VAL_UNKNOWN,
409 ac_online: 1,
410 int_enable: 0,
411 int_status: 0,
412 voltage: 0,
413 current: 0,
414 charge_counter: 0,
415 charge_full: 0,
416 bat_config: BatConfig::Real,
417 }));
418
419 Ok(GoldfishBattery {
420 state,
421 mmio_base: mmio_base as u32,
422 irq_num,
423 irq_evt,
424 activated: false,
425 monitor_thread: None,
426 tube: Some(tube),
427 create_power_monitor,
428 create_powerd_client,
429 init_state: Arc::new(Mutex::new(BatInitializationState::NotYet)),
430 })
431 }
432
433 pub fn keep_rds(&self) -> Vec<RawDescriptor> {
435 let mut rds = vec![
436 self.irq_evt.get_trigger().as_raw_descriptor(),
437 self.irq_evt.get_resample().as_raw_descriptor(),
438 ];
439
440 if let Some(tube) = &self.tube {
441 rds.push(tube.as_raw_descriptor());
442 }
443
444 rds
445 }
446
447 fn start_monitor(&mut self) {
449 if self.activated {
450 return;
451 }
452
453 if let Some(tube) = self.tube.take() {
454 let irq_evt = self.irq_evt.try_clone().unwrap();
455 let bat_state = self.state.clone();
456 let create_monitor_fn = self.create_power_monitor.take();
457 let init_state = self.init_state.clone();
458 self.monitor_thread = Some(WorkerThread::start(self.debug_label(), move |kill_evt| {
459 command_monitor(
460 tube,
461 irq_evt,
462 kill_evt,
463 bat_state,
464 create_monitor_fn,
465 init_state,
466 )
467 }));
468 self.activated = true;
469 }
470 }
471
472 fn initialize_battery_state(&mut self) -> anyhow::Result<()> {
473 let mut init_state = self.init_state.lock();
474 let power_client = match (init_state.deref_mut(), &self.create_powerd_client) {
475 (BatInitializationState::NotYet, None) => {
476 return Ok(());
478 }
479 (BatInitializationState::NotYet, Some(f)) => {
480 let client = match f() {
482 Ok(c) => c,
483 Err(e) => bail!("failed to connect to the powerd: {:#}", e),
484 };
485 *init_state = BatInitializationState::Pending(client);
487
488 let power_client = match init_state.deref_mut() {
489 BatInitializationState::Pending(ref mut power_client) => power_client.as_mut(),
490 _ => unreachable!("init_state should be Pending"),
491 };
492 power_client
493 }
494 (BatInitializationState::Pending(ref mut power_client), _) => power_client.as_mut(),
495 (BatInitializationState::Done, _) => return Ok(()),
498 };
499
500 if let Some(prev_call) = power_client.last_request_timestamp() {
501 let now = SystemTime::now();
503 let duration = now
504 .duration_since(prev_call)
505 .context("failed to calculate time for dbus request")?;
506 if duration < Duration::from_millis(Self::POWERD_REQ_INTERVAL_MS) {
507 return Ok(());
508 }
509 }
510
511 let BatInitializationState::Pending(mut power_client) =
514 std::mem::replace(&mut *init_state, BatInitializationState::NotYet)
515 else {
516 unreachable!("init_state should be Pending");
517 };
518 drop(init_state);
519
520 let data = match power_client.get_power_data() {
521 Ok(data) => data,
522 Err(e) => {
523 let mut init_state = self.init_state.lock();
524 if matches!(*init_state, BatInitializationState::Done) {
525 return Ok(());
526 }
527 *init_state = BatInitializationState::Pending(power_client);
528 bail!("failed to get response from powerd: {:#}", e);
529 }
530 };
531
532 drop(power_client);
533
534 let mut init_state = self.init_state.lock();
538 if matches!(*init_state, BatInitializationState::Done) {
539 return Ok(());
540 }
541 *init_state = BatInitializationState::Done;
542
543 self.state.lock().update_from_power_data(data);
544 Ok(())
545 }
546
547 fn battery_init_done(&self) -> bool {
548 matches!(*self.init_state.lock(), BatInitializationState::Done)
549 }
550}
551
552impl Drop for GoldfishBattery {
553 fn drop(&mut self) {
554 if let Err(e) = self.sleep() {
555 error!("{}", e);
556 };
557 }
558}
559
560impl BusDevice for GoldfishBattery {
561 fn device_id(&self) -> DeviceId {
562 PlatformDeviceId::GoldfishBattery.into()
563 }
564
565 fn debug_label(&self) -> String {
566 "GoldfishBattery".to_owned()
567 }
568
569 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
570 if data.len() != std::mem::size_of::<u32>() {
571 warn!(
572 "{}: unsupported read length {}, only support 4bytes read",
573 self.debug_label(),
574 data.len()
575 );
576 return;
577 }
578
579 if !self.battery_init_done() {
581 if let Err(e) = self.initialize_battery_state() {
582 error!(
583 "{}: failed to initialize bettery state (info={:?}): {:#}",
584 self.debug_label(),
585 info,
586 e
587 );
588 }
589 }
590
591 let val = match info.offset as u32 {
592 BATTERY_INT_STATUS => {
593 std::mem::replace(&mut self.state.lock().int_status, 0)
595 }
596 BATTERY_INT_ENABLE => self.state.lock().int_enable,
597 BATTERY_AC_ONLINE => {
598 let bat_config = self.state.lock().bat_config;
599 match bat_config {
600 BatConfig::Real => self.state.lock().ac_online,
601 BatConfig::Fake { max_capacity: _ } => AC_ONLINE_VAL_OFFLINE,
602 }
603 }
604 BATTERY_STATUS => {
605 let bat_config = self.state.lock().bat_config;
606 match bat_config {
607 BatConfig::Real => self.state.lock().status,
608 BatConfig::Fake { max_capacity: _ } => BATTERY_STATUS_VAL_DISCHARGING,
609 }
610 }
611 BATTERY_HEALTH => self.state.lock().health,
612 BATTERY_PRESENT => self.state.lock().present,
613 BATTERY_CAPACITY => {
614 let max_capacity = match self.state.lock().bat_config {
615 BatConfig::Real => 100,
616 BatConfig::Fake { max_capacity } => max_capacity,
617 };
618 std::cmp::min(max_capacity, self.state.lock().capacity)
619 }
620 BATTERY_VOLTAGE => self.state.lock().voltage,
621 BATTERY_TEMP => 0,
622 BATTERY_CHARGE_COUNTER => self.state.lock().charge_counter,
623 BATTERY_VOLTAGE_MAX => 0,
624 BATTERY_CURRENT_MAX => 0,
625 BATTERY_CURRENT_NOW => self.state.lock().current,
626 BATTERY_CURRENT_AVG => 0,
627 BATTERY_CHARGE_FULL_UAH => self.state.lock().charge_full,
628 BATTERY_CYCLE_COUNT => 0,
629 _ => {
630 warn!("{}: unsupported read address {}", self.debug_label(), info);
631 return;
632 }
633 };
634
635 let val_arr = val.to_ne_bytes();
636 data.copy_from_slice(&val_arr);
637 }
638
639 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
640 if data.len() != std::mem::size_of::<u32>() {
641 warn!(
642 "{}: unsupported write length {}, only support 4bytes write",
643 self.debug_label(),
644 data.len()
645 );
646 return;
647 }
648
649 let mut val_arr = u32::to_ne_bytes(0u32);
650 val_arr.copy_from_slice(data);
651 let val = u32::from_ne_bytes(val_arr);
652
653 match info.offset as u32 {
654 BATTERY_INT_ENABLE => {
655 self.state.lock().int_enable = val;
656 if (val & BATTERY_INT_MASK) != 0 && !self.activated {
657 self.start_monitor();
658 }
659 }
660 _ => {
661 warn!("{}: Bad write to address {}", self.debug_label(), info);
662 }
663 };
664 }
665}
666
667impl Aml for GoldfishBattery {
668 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
669 aml::Device::new(
670 "GFBY".into(),
671 vec![
672 &aml::Name::new("_HID".into(), &"GFSH0001"),
673 &aml::Name::new(
674 "_CRS".into(),
675 &aml::ResourceTemplate::new(vec![
676 &aml::Memory32Fixed::new(true, self.mmio_base, GOLDFISHBAT_MMIO_LEN as u32),
677 &aml::Interrupt::new(true, false, false, true, self.irq_num),
678 ]),
679 ),
680 ],
681 )
682 .to_aml_bytes(bytes);
683 }
684}
685
686impl Suspendable for GoldfishBattery {
687 fn sleep(&mut self) -> anyhow::Result<()> {
688 if let Some(thread) = self.monitor_thread.take() {
689 thread.stop();
690 }
691 Ok(())
692 }
693
694 fn wake(&mut self) -> anyhow::Result<()> {
695 if self.activated {
696 self.activated = false;
698 self.start_monitor();
699 }
700 Ok(())
701 }
702
703 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
704 AnySnapshot::to_any(GoldfishBatterySnapshot {
705 state: self.state.lock().clone(),
706 mmio_base: self.mmio_base,
707 irq_num: self.irq_num,
708 activated: self.activated,
709 })
710 .context("failed to snapshot GoldfishBattery")
711 }
712
713 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
714 let deser: GoldfishBatterySnapshot =
715 AnySnapshot::from_any(data).context("failed to deserialize GoldfishBattery")?;
716 {
717 let mut locked_state = self.state.lock();
718 *locked_state = deser.state;
719 }
720 self.mmio_base = deser.mmio_base;
721 self.irq_num = deser.irq_num;
722 self.activated = deser.activated;
723 Ok(())
724 }
725}
726
727#[cfg(test)]
728mod tests {
729 use std::time::SystemTime;
730
731 use super::*;
732 use crate::suspendable_tests;
733
734 fn modify_device(battery: &mut GoldfishBattery) {
735 let mut state = battery.state.lock();
736 state.set_capacity(70);
737 }
738
739 suspendable_tests! {
740 battery, GoldfishBattery::new(
741 0,
742 0,
743 IrqLevelEvent::new().unwrap(),
744 Tube::pair().unwrap().1,
745 None,
746 None,
747 ).unwrap(),
748 modify_device
749 }
750
751 struct MockPowerClient {
753 last_request_time: Option<SystemTime>,
754 }
755
756 impl MockPowerClient {
757 fn new(last_request_time: Option<SystemTime>) -> Self {
758 Self { last_request_time }
759 }
760 }
761
762 impl power_monitor::PowerClient for MockPowerClient {
763 fn last_request_timestamp(&self) -> Option<SystemTime> {
764 self.last_request_time
765 }
766
767 fn get_power_data(
768 &mut self,
769 ) -> std::result::Result<power_monitor::PowerData, Box<dyn std::error::Error>> {
770 self.last_request_time = Some(SystemTime::now());
771 Ok(power_monitor::PowerData {
772 ac_online: true,
773 battery: Some(power_monitor::BatteryData {
774 percent: 50,
775 status: power_monitor::BatteryStatus::Unknown,
776 voltage: 0,
777 current: 0,
778 charge_counter: 0,
779 charge_full: 0,
780 }),
781 })
782 }
783 }
784
785 fn create_mock_power_client(last_request_time: Option<SystemTime>) -> MockPowerClient {
786 MockPowerClient::new(last_request_time)
787 }
788
789 #[test]
790 fn test_initialize_battery_state_rate_limiting() {
791 let mmio_base = 0;
792 let irq_num = 0;
793 let irq_evt = IrqLevelEvent::new().unwrap();
794 let tube = Tube::pair().unwrap().1;
795
796 let now = SystemTime::now();
797 let recent_time =
798 now - Duration::from_millis(GoldfishBattery::POWERD_REQ_INTERVAL_MS - 500);
799
800 let mut battery = GoldfishBattery::new(
801 mmio_base,
802 irq_num,
803 irq_evt,
804 tube,
805 None,
806 Some(Box::new(move || {
807 Ok(Box::new(create_mock_power_client(Some(recent_time))))
808 })),
809 )
810 .unwrap();
811
812 assert!(matches!(battery.initialize_battery_state(), Ok(())));
813
814 assert!(matches!(
816 *battery.init_state.lock(),
817 BatInitializationState::Pending(_)
818 ));
819
820 *battery.init_state.lock() = BatInitializationState::NotYet;
821 let old_time = now - Duration::from_millis(GoldfishBattery::POWERD_REQ_INTERVAL_MS + 500);
822 battery.create_powerd_client = Some(Box::new(move || {
824 Ok(Box::new(create_mock_power_client(Some(old_time))))
825 }));
826
827 assert!(matches!(battery.initialize_battery_state(), Ok(())));
829
830 assert!(matches!(
831 *battery.init_state.lock(),
832 BatInitializationState::Done,
833 ));
834
835 let state = battery.state.lock();
837 assert_eq!(state.ac_online, 1);
838 assert_eq!(state.capacity, 50);
839 }
840
841 #[test]
842 fn test_initialize_battery_state_no_powerd_client() {
843 let mmio_base = 0;
844 let irq_num = 0;
845 let irq_evt = IrqLevelEvent::new().unwrap();
846 let tube = Tube::pair().unwrap().1;
847
848 let mut battery =
849 GoldfishBattery::new(mmio_base, irq_num, irq_evt, tube, None, None).unwrap();
850
851 assert!(matches!(battery.initialize_battery_state(), Ok(())));
852 assert!(matches!(
853 *battery.init_state.lock(),
854 BatInitializationState::NotYet,
855 ));
856 }
857
858 struct BlockingPowerMonitor {
859 poll_evt: Event,
860 }
861
862 impl ReadNotifier for BlockingPowerMonitor {
863 fn get_read_notifier(&self) -> &dyn AsRawDescriptor {
864 &self.poll_evt
865 }
866 }
867
868 impl power_monitor::PowerMonitor for BlockingPowerMonitor {
869 fn read_message(
870 &mut self,
871 ) -> std::result::Result<Option<power_monitor::PowerData>, Box<dyn std::error::Error>>
872 {
873 self.poll_evt.wait().unwrap();
874 Ok(Some(power_monitor::PowerData {
875 ac_online: false,
876 battery: Some(power_monitor::BatteryData {
877 percent: 80,
878 status: power_monitor::BatteryStatus::Discharging,
879 voltage: 0,
880 current: 0,
881 charge_counter: 0,
882 charge_full: 0,
883 }),
884 }))
885 }
886 }
887
888 struct RacingPowerClient {
889 poll_evt: Event,
890 irq_trigger_evt: Event,
891 fail: bool,
892 }
893
894 impl power_monitor::PowerClient for RacingPowerClient {
895 fn last_request_timestamp(&self) -> Option<SystemTime> {
896 None
897 }
898
899 fn get_power_data(
900 &mut self,
901 ) -> std::result::Result<power_monitor::PowerData, Box<dyn std::error::Error>> {
902 self.poll_evt.signal().unwrap();
906 self.irq_trigger_evt.wait().unwrap();
907 if self.fail {
908 return Err("mock powerd error".into());
909 }
910 Ok(power_monitor::PowerData {
911 ac_online: true,
912 battery: Some(power_monitor::BatteryData {
913 percent: 50,
914 status: power_monitor::BatteryStatus::Charging,
915 voltage: 0,
916 current: 0,
917 charge_counter: 0,
918 charge_full: 0,
919 }),
920 })
921 }
922 }
923
924 #[test]
925 fn test_concurrent_monitor_and_initialize_no_deadlock() {
926 let irq_evt = IrqLevelEvent::new().unwrap();
927 let irq_trigger_evt = irq_evt.get_trigger().try_clone().unwrap();
928 let (_control_tube, device_tube) = Tube::pair().unwrap();
929 let poll_evt = Event::new().unwrap();
930
931 let poll_evt_monitor = poll_evt.try_clone().unwrap();
932 let create_monitor: Box<dyn CreatePowerMonitorFn> = Box::new(move || {
933 Ok(Box::new(BlockingPowerMonitor {
934 poll_evt: poll_evt_monitor.try_clone().unwrap(),
935 }))
936 });
937
938 let create_client: Box<dyn CreatePowerClientFn> = Box::new(move || {
939 Ok(Box::new(RacingPowerClient {
940 poll_evt: poll_evt.try_clone().unwrap(),
941 irq_trigger_evt: irq_trigger_evt.try_clone().unwrap(),
942 fail: false,
943 }))
944 });
945
946 let mut battery = GoldfishBattery::new(
947 0,
948 0,
949 irq_evt,
950 device_tube,
951 Some(create_monitor),
952 Some(create_client),
953 )
954 .unwrap();
955
956 battery.state.lock().present = 0;
959 battery.state.lock().int_enable = BATTERY_INT_MASK;
960 battery.start_monitor();
961
962 battery.initialize_battery_state().unwrap();
966 assert!(matches!(
967 *battery.init_state.lock(),
968 BatInitializationState::Done
969 ));
970
971 battery.initialize_battery_state().unwrap();
973
974 let state = battery.state.lock();
978 assert_eq!(state.present, 1);
979 assert_eq!(state.ac_online, 0);
980 assert_eq!(state.capacity, 80);
981 assert_eq!(state.status, BATTERY_STATUS_VAL_DISCHARGING);
982 }
983
984 struct FailingPowerClient {
985 last_request_time: Option<SystemTime>,
986 }
987
988 impl power_monitor::PowerClient for FailingPowerClient {
989 fn last_request_timestamp(&self) -> Option<SystemTime> {
990 self.last_request_time
991 }
992
993 fn get_power_data(
994 &mut self,
995 ) -> std::result::Result<power_monitor::PowerData, Box<dyn std::error::Error>> {
996 self.last_request_time = Some(SystemTime::now());
997 Err("mock powerd failure".into())
998 }
999 }
1000
1001 #[test]
1002 fn test_initialize_battery_state_error_restores_pending_unless_done() {
1003 let irq_evt = IrqLevelEvent::new().unwrap();
1004 let (_control_tube, device_tube) = Tube::pair().unwrap();
1005
1006 let mut battery = GoldfishBattery::new(
1007 0,
1008 0,
1009 irq_evt,
1010 device_tube,
1011 None,
1012 Some(Box::new(|| {
1013 Ok(Box::new(FailingPowerClient {
1014 last_request_time: None,
1015 }))
1016 })),
1017 )
1018 .unwrap();
1019
1020 assert!(battery.initialize_battery_state().is_err());
1023 assert!(matches!(
1024 *battery.init_state.lock(),
1025 BatInitializationState::Pending(ref client) if client.last_request_timestamp().is_some()
1026 ));
1027 battery.initialize_battery_state().unwrap();
1029
1030 let irq_evt = IrqLevelEvent::new().unwrap();
1034 let irq_trigger_evt = irq_evt.get_trigger().try_clone().unwrap();
1035 let (_control_tube, device_tube) = Tube::pair().unwrap();
1036 let poll_evt = Event::new().unwrap();
1037
1038 let poll_evt_monitor = poll_evt.try_clone().unwrap();
1039 let create_monitor: Box<dyn CreatePowerMonitorFn> = Box::new(move || {
1040 Ok(Box::new(BlockingPowerMonitor {
1041 poll_evt: poll_evt_monitor.try_clone().unwrap(),
1042 }))
1043 });
1044
1045 let create_client: Box<dyn CreatePowerClientFn> = Box::new(move || {
1046 Ok(Box::new(RacingPowerClient {
1047 poll_evt: poll_evt.try_clone().unwrap(),
1048 irq_trigger_evt: irq_trigger_evt.try_clone().unwrap(),
1049 fail: true,
1050 }))
1051 });
1052
1053 let mut battery = GoldfishBattery::new(
1054 0,
1055 0,
1056 irq_evt,
1057 device_tube,
1058 Some(create_monitor),
1059 Some(create_client),
1060 )
1061 .unwrap();
1062
1063 battery.state.lock().int_enable = BATTERY_INT_MASK;
1064 battery.start_monitor();
1065 assert!(matches!(battery.initialize_battery_state(), Ok(())));
1066 assert!(matches!(
1067 *battery.init_state.lock(),
1068 BatInitializationState::Done
1069 ));
1070 let state = battery.state.lock();
1071 assert_eq!(state.ac_online, 0);
1072 assert_eq!(state.capacity, 80);
1073 assert_eq!(state.status, BATTERY_STATUS_VAL_DISCHARGING);
1074 }
1075}