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::Tube;
21use base::WaitContext;
22use base::WorkerThread;
23use power_monitor::BatteryStatus;
24use power_monitor::CreatePowerClientFn;
25use power_monitor::CreatePowerMonitorFn;
26use power_monitor::PowerClient;
27use remain::sorted;
28use serde::Deserialize;
29use serde::Serialize;
30use snapshot::AnySnapshot;
31use sync::Mutex;
32use thiserror::Error;
33use vm_control::BatControlCommand;
34use vm_control::BatControlResult;
35
36use crate::pci::CrosvmDeviceId;
37use crate::BusAccessInfo;
38use crate::BusDevice;
39use crate::DeviceId;
40use crate::IrqLevelEvent;
41use crate::Suspendable;
42
43#[sorted]
45#[derive(Error, Debug)]
46pub enum BatteryError {
47 #[error("Non 32-bit mmio address space")]
48 Non32BitMmioAddress,
49}
50
51type Result<T> = std::result::Result<T, BatteryError>;
52
53pub const GOLDFISHBAT_MMIO_LEN: u64 = 0x1000;
55
56#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, Copy)]
58pub enum BatConfig {
59 #[default]
61 Real,
62 Fake {
66 max_capacity: u32,
68 },
69}
70
71#[derive(Clone, Serialize, Deserialize)]
72struct GoldfishBatteryState {
73 int_status: u32,
75 int_enable: u32,
76 ac_online: u32,
78 status: u32,
80 health: u32,
81 present: u32,
82 capacity: u32,
83 voltage: u32,
84 current: u32,
85 charge_counter: u32,
86 charge_full: u32,
87 bat_config: BatConfig,
89}
90
91macro_rules! create_battery_func {
92 ($fn:ident, $property:ident, $ty:ty, $int:ident) => {
96 pub(crate) fn $fn(&mut self, value: $ty) -> bool {
97 let old = std::mem::replace(&mut self.$property, value);
98 old != self.$property && self.set_int_status($int)
99 }
100 };
101}
102
103impl GoldfishBatteryState {
104 fn set_int_status(&mut self, mask: u32) -> bool {
105 let old = self.int_status;
106 self.int_status |= self.int_enable & mask;
107 old != self.int_status
108 }
109
110 fn int_status(&self) -> u32 {
111 self.int_status
112 }
113
114 create_battery_func!(set_ac_online, ac_online, u32, AC_STATUS_CHANGED);
115
116 create_battery_func!(set_status, status, u32, BATTERY_STATUS_CHANGED);
117
118 create_battery_func!(set_health, health, u32, BATTERY_STATUS_CHANGED);
119
120 create_battery_func!(set_present, present, u32, BATTERY_STATUS_CHANGED);
121
122 create_battery_func!(set_capacity, capacity, u32, BATTERY_STATUS_CHANGED);
123
124 create_battery_func!(set_voltage, voltage, u32, BATTERY_STATUS_CHANGED);
125
126 create_battery_func!(set_current, current, u32, BATTERY_STATUS_CHANGED);
127
128 create_battery_func!(
129 set_charge_counter,
130 charge_counter,
131 u32,
132 BATTERY_STATUS_CHANGED
133 );
134
135 create_battery_func!(set_charge_full, charge_full, u32, BATTERY_STATUS_CHANGED);
136
137 create_battery_func!(set_bat_config, bat_config, BatConfig, BATTERY_INT_MASK);
138}
139
140enum BatInitializationState {
141 NotYet,
142 Pending(Box<dyn PowerClient>),
143 Done,
144}
145
146pub struct GoldfishBattery {
148 state: Arc<Mutex<GoldfishBatteryState>>,
149 mmio_base: u32,
150 irq_num: u32,
151 irq_evt: IrqLevelEvent,
152 activated: bool,
153 monitor_thread: Option<WorkerThread<()>>,
154 tube: Option<Tube>,
155 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
156 create_powerd_client: Option<Box<dyn CreatePowerClientFn>>,
157 init_state: Arc<Mutex<BatInitializationState>>,
158}
159
160#[derive(Serialize, Deserialize)]
161struct GoldfishBatterySnapshot {
162 state: GoldfishBatteryState,
163 mmio_base: u32,
164 irq_num: u32,
165 activated: bool,
166}
167
168const BATTERY_INT_STATUS: u32 = 0;
170const BATTERY_INT_ENABLE: u32 = 0x4;
171const BATTERY_AC_ONLINE: u32 = 0x8;
172const BATTERY_STATUS: u32 = 0xC;
173const BATTERY_HEALTH: u32 = 0x10;
174const BATTERY_PRESENT: u32 = 0x14;
175const BATTERY_CAPACITY: u32 = 0x18;
176const BATTERY_VOLTAGE: u32 = 0x1C;
177const BATTERY_TEMP: u32 = 0x20;
178const BATTERY_CHARGE_COUNTER: u32 = 0x24;
179const BATTERY_VOLTAGE_MAX: u32 = 0x28;
180const BATTERY_CURRENT_MAX: u32 = 0x2C;
181const BATTERY_CURRENT_NOW: u32 = 0x30;
182const BATTERY_CURRENT_AVG: u32 = 0x34;
183const BATTERY_CHARGE_FULL_UAH: u32 = 0x38;
184const BATTERY_CYCLE_COUNT: u32 = 0x40;
185
186const BATTERY_STATUS_CHANGED: u32 = 1 << 0;
188const AC_STATUS_CHANGED: u32 = 1 << 1;
189const BATTERY_INT_MASK: u32 = BATTERY_STATUS_CHANGED | AC_STATUS_CHANGED;
190
191const BATTERY_STATUS_VAL_UNKNOWN: u32 = 0;
193const BATTERY_STATUS_VAL_CHARGING: u32 = 1;
194const BATTERY_STATUS_VAL_DISCHARGING: u32 = 2;
195const BATTERY_STATUS_VAL_NOT_CHARGING: u32 = 3;
196
197const BATTERY_HEALTH_VAL_UNKNOWN: u32 = 0;
199
200const AC_ONLINE_VAL_OFFLINE: u32 = 0;
202
203#[derive(EventToken)]
204pub(crate) enum Token {
205 Commands,
206 Resample,
207 Kill,
208 Monitor,
209}
210
211fn command_monitor(
212 tube: Tube,
213 irq_evt: IrqLevelEvent,
214 kill_evt: Event,
215 state: Arc<Mutex<GoldfishBatteryState>>,
216 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
217 init_state: Arc<Mutex<BatInitializationState>>,
218) {
219 let wait_ctx: WaitContext<Token> = match WaitContext::build_with(&[
220 (&tube, Token::Commands),
221 (irq_evt.get_resample(), Token::Resample),
222 (&kill_evt, Token::Kill),
223 ]) {
224 Ok(pc) => pc,
225 Err(e) => {
226 error!("failed to build WaitContext: {}", e);
227 return;
228 }
229 };
230
231 let mut power_monitor = match create_power_monitor {
232 Some(f) => match f() {
233 Ok(p) => match wait_ctx.add(p.get_read_notifier(), Token::Monitor) {
234 Ok(()) => Some(p),
235 Err(e) => {
236 error!("failed to add power monitor to poll context: {}", e);
237 None
238 }
239 },
240 Err(e) => {
241 error!("failed to create power monitor: {}", e);
242 None
243 }
244 },
245 None => None,
246 };
247
248 'poll: loop {
249 let events = match wait_ctx.wait() {
250 Ok(v) => v,
251 Err(e) => {
252 error!("error while polling for events: {}", e);
253 break;
254 }
255 };
256
257 for event in events.iter().filter(|e| e.is_readable) {
258 match event.token {
259 Token::Commands => {
260 let req = match tube.recv() {
261 Ok(req) => req,
262 Err(e) => {
263 error!("failed to receive request: {}", e);
264 continue;
265 }
266 };
267
268 let mut bat_state = state.lock();
269 let inject_irq = match req {
270 BatControlCommand::SetStatus(status) => bat_state.set_status(status.into()),
271 BatControlCommand::SetHealth(health) => bat_state.set_health(health.into()),
272 BatControlCommand::SetPresent(present) => {
273 let v = present != 0;
274 bat_state.set_present(v.into())
275 }
276 BatControlCommand::SetCapacity(capacity) => {
277 let v = std::cmp::min(capacity, 100);
278 bat_state.set_capacity(v)
279 }
280 BatControlCommand::SetACOnline(ac_online) => {
281 let v = ac_online != 0;
282 bat_state.set_ac_online(v.into())
283 }
284 BatControlCommand::SetFakeBatConfig(max_capacity) => {
285 let max_capacity = std::cmp::min(max_capacity, 100);
286 bat_state.set_bat_config(BatConfig::Fake { max_capacity })
287 }
288 BatControlCommand::CancelFakeConfig => {
289 bat_state.set_bat_config(BatConfig::Real)
290 }
291 };
292
293 if inject_irq {
294 let _ = irq_evt.trigger();
295 }
296
297 if let Err(e) = tube.send(&BatControlResult::Ok) {
298 error!("failed to send response: {}", e);
299 }
300 }
301
302 Token::Monitor => {
303 let power_monitor = power_monitor.as_mut().unwrap();
305
306 let data = match power_monitor.read_message() {
307 Ok(Some(d)) => d,
308 Ok(None) => continue,
309 Err(e) => {
310 error!("failed to read new power data: {}", e);
311 continue;
312 }
313 };
314
315 let mut bat_state = state.lock();
316
317 let mut inject_irq = bat_state.set_ac_online(data.ac_online.into());
321
322 match data.battery {
323 Some(battery_data) => {
324 inject_irq |= bat_state.set_capacity(battery_data.percent);
325 let battery_status = match battery_data.status {
326 BatteryStatus::Unknown => BATTERY_STATUS_VAL_UNKNOWN,
327 BatteryStatus::Charging => BATTERY_STATUS_VAL_CHARGING,
328 BatteryStatus::Discharging => BATTERY_STATUS_VAL_DISCHARGING,
329 BatteryStatus::NotCharging => BATTERY_STATUS_VAL_NOT_CHARGING,
330 };
331 inject_irq |= bat_state.set_status(battery_status);
332 inject_irq |= bat_state.set_voltage(battery_data.voltage);
333 inject_irq |= bat_state.set_current(battery_data.current);
334 inject_irq |= bat_state.set_charge_counter(battery_data.charge_counter);
335 inject_irq |= bat_state.set_charge_full(battery_data.charge_full);
336 }
337 None => {
338 inject_irq |= bat_state.set_present(0);
339 }
340 }
341 *init_state.lock() = BatInitializationState::Done;
342
343 if inject_irq {
344 let _ = irq_evt.trigger();
345 }
346 }
347
348 Token::Resample => {
349 irq_evt.clear_resample();
350 if state.lock().int_status() != 0 {
351 let _ = irq_evt.trigger();
352 }
353 }
354
355 Token::Kill => break 'poll,
356 }
357 }
358 }
359}
360
361impl GoldfishBattery {
362 pub(crate) const POWERD_REQ_INTERVAL_MS: u64 = 1000;
365
366 pub fn new(
375 mmio_base: u64,
376 irq_num: u32,
377 irq_evt: IrqLevelEvent,
378 tube: Tube,
379 create_power_monitor: Option<Box<dyn CreatePowerMonitorFn>>,
380 create_powerd_client: Option<Box<dyn CreatePowerClientFn>>,
381 ) -> Result<Self> {
382 if mmio_base + GOLDFISHBAT_MMIO_LEN - 1 > u32::MAX as u64 {
383 return Err(BatteryError::Non32BitMmioAddress);
384 }
385 let state = Arc::new(Mutex::new(GoldfishBatteryState {
386 capacity: 50,
387 health: BATTERY_HEALTH_VAL_UNKNOWN,
388 present: 1,
389 status: BATTERY_STATUS_VAL_UNKNOWN,
390 ac_online: 1,
391 int_enable: 0,
392 int_status: 0,
393 voltage: 0,
394 current: 0,
395 charge_counter: 0,
396 charge_full: 0,
397 bat_config: BatConfig::Real,
398 }));
399
400 Ok(GoldfishBattery {
401 state,
402 mmio_base: mmio_base as u32,
403 irq_num,
404 irq_evt,
405 activated: false,
406 monitor_thread: None,
407 tube: Some(tube),
408 create_power_monitor,
409 create_powerd_client,
410 init_state: Arc::new(Mutex::new(BatInitializationState::NotYet)),
411 })
412 }
413
414 pub fn keep_rds(&self) -> Vec<RawDescriptor> {
416 let mut rds = vec![
417 self.irq_evt.get_trigger().as_raw_descriptor(),
418 self.irq_evt.get_resample().as_raw_descriptor(),
419 ];
420
421 if let Some(tube) = &self.tube {
422 rds.push(tube.as_raw_descriptor());
423 }
424
425 rds
426 }
427
428 fn start_monitor(&mut self) {
430 if self.activated {
431 return;
432 }
433
434 if let Some(tube) = self.tube.take() {
435 let irq_evt = self.irq_evt.try_clone().unwrap();
436 let bat_state = self.state.clone();
437 let create_monitor_fn = self.create_power_monitor.take();
438 let init_state = self.init_state.clone();
439 self.monitor_thread = Some(WorkerThread::start(self.debug_label(), move |kill_evt| {
440 command_monitor(
441 tube,
442 irq_evt,
443 kill_evt,
444 bat_state,
445 create_monitor_fn,
446 init_state,
447 )
448 }));
449 self.activated = true;
450 }
451 }
452
453 fn initialize_battery_state(&mut self) -> anyhow::Result<()> {
454 let mut init_state = self.init_state.lock();
455 let power_client = match (init_state.deref_mut(), &self.create_powerd_client) {
456 (BatInitializationState::NotYet, None) => {
457 return Ok(());
459 }
460 (BatInitializationState::NotYet, Some(f)) => {
461 let client = match f() {
463 Ok(c) => c,
464 Err(e) => bail!("failed to connect to the powerd: {:#}", e),
465 };
466 *init_state = BatInitializationState::Pending(client);
468
469 let power_client = match init_state.deref_mut() {
470 BatInitializationState::Pending(ref mut power_client) => power_client.as_mut(),
471 _ => unreachable!("init_state should be Pending"),
472 };
473 power_client
474 }
475 (BatInitializationState::Pending(ref mut power_client), _) => power_client.as_mut(),
476 (BatInitializationState::Done, _) => bail!("battery status already intialized"),
477 };
478
479 if let Some(prev_call) = power_client.last_request_timestamp() {
480 let now = SystemTime::now();
482 let duration = now
483 .duration_since(prev_call)
484 .context("failed to calculate time for dbus request")?;
485 if duration < Duration::from_millis(Self::POWERD_REQ_INTERVAL_MS) {
486 return Ok(());
487 }
488 }
489
490 match power_client.get_power_data() {
491 Ok(data) => {
492 let mut bat_state = self.state.lock();
493 bat_state.set_ac_online(data.ac_online.into());
494
495 match data.battery {
496 Some(battery_data) => {
497 bat_state.set_capacity(battery_data.percent);
498 let battery_status = match battery_data.status {
499 BatteryStatus::Unknown => BATTERY_STATUS_VAL_UNKNOWN,
500 BatteryStatus::Charging => BATTERY_STATUS_VAL_CHARGING,
501 BatteryStatus::Discharging => BATTERY_STATUS_VAL_DISCHARGING,
502 BatteryStatus::NotCharging => BATTERY_STATUS_VAL_NOT_CHARGING,
503 };
504 bat_state.set_status(battery_status);
505 bat_state.set_voltage(battery_data.voltage);
506 bat_state.set_current(battery_data.current);
507 bat_state.set_charge_counter(battery_data.charge_counter);
508 bat_state.set_charge_full(battery_data.charge_full);
509 }
510 None => {
511 bat_state.set_present(0);
512 }
513 }
514 }
515 Err(e) => {
516 bail!("failed to get response from powerd: {:#}", e);
517 }
518 };
519 *init_state = BatInitializationState::Done;
521 Ok(())
522 }
523
524 fn battery_init_done(&self) -> bool {
525 matches!(*self.init_state.lock(), BatInitializationState::Done)
526 }
527}
528
529impl Drop for GoldfishBattery {
530 fn drop(&mut self) {
531 if let Err(e) = self.sleep() {
532 error!("{}", e);
533 };
534 }
535}
536
537impl BusDevice for GoldfishBattery {
538 fn device_id(&self) -> DeviceId {
539 CrosvmDeviceId::GoldfishBattery.into()
540 }
541
542 fn debug_label(&self) -> String {
543 "GoldfishBattery".to_owned()
544 }
545
546 fn read(&mut self, info: BusAccessInfo, data: &mut [u8]) {
547 if data.len() != std::mem::size_of::<u32>() {
548 warn!(
549 "{}: unsupported read length {}, only support 4bytes read",
550 self.debug_label(),
551 data.len()
552 );
553 return;
554 }
555
556 if !self.battery_init_done() {
558 if let Err(e) = self.initialize_battery_state() {
559 error!(
560 "{}: failed to initialize bettery state (info={:?}): {:#}",
561 self.debug_label(),
562 info,
563 e
564 );
565 }
566 }
567
568 let val = match info.offset as u32 {
569 BATTERY_INT_STATUS => {
570 std::mem::replace(&mut self.state.lock().int_status, 0)
572 }
573 BATTERY_INT_ENABLE => self.state.lock().int_enable,
574 BATTERY_AC_ONLINE => {
575 let bat_config = self.state.lock().bat_config;
576 match bat_config {
577 BatConfig::Real => self.state.lock().ac_online,
578 BatConfig::Fake { max_capacity: _ } => AC_ONLINE_VAL_OFFLINE,
579 }
580 }
581 BATTERY_STATUS => {
582 let bat_config = self.state.lock().bat_config;
583 match bat_config {
584 BatConfig::Real => self.state.lock().status,
585 BatConfig::Fake { max_capacity: _ } => BATTERY_STATUS_VAL_DISCHARGING,
586 }
587 }
588 BATTERY_HEALTH => self.state.lock().health,
589 BATTERY_PRESENT => self.state.lock().present,
590 BATTERY_CAPACITY => {
591 let max_capacity = match self.state.lock().bat_config {
592 BatConfig::Real => 100,
593 BatConfig::Fake { max_capacity } => max_capacity,
594 };
595 std::cmp::min(max_capacity, self.state.lock().capacity)
596 }
597 BATTERY_VOLTAGE => self.state.lock().voltage,
598 BATTERY_TEMP => 0,
599 BATTERY_CHARGE_COUNTER => self.state.lock().charge_counter,
600 BATTERY_VOLTAGE_MAX => 0,
601 BATTERY_CURRENT_MAX => 0,
602 BATTERY_CURRENT_NOW => self.state.lock().current,
603 BATTERY_CURRENT_AVG => 0,
604 BATTERY_CHARGE_FULL_UAH => self.state.lock().charge_full,
605 BATTERY_CYCLE_COUNT => 0,
606 _ => {
607 warn!("{}: unsupported read address {}", self.debug_label(), info);
608 return;
609 }
610 };
611
612 let val_arr = val.to_ne_bytes();
613 data.copy_from_slice(&val_arr);
614 }
615
616 fn write(&mut self, info: BusAccessInfo, data: &[u8]) {
617 if data.len() != std::mem::size_of::<u32>() {
618 warn!(
619 "{}: unsupported write length {}, only support 4bytes write",
620 self.debug_label(),
621 data.len()
622 );
623 return;
624 }
625
626 let mut val_arr = u32::to_ne_bytes(0u32);
627 val_arr.copy_from_slice(data);
628 let val = u32::from_ne_bytes(val_arr);
629
630 match info.offset as u32 {
631 BATTERY_INT_ENABLE => {
632 self.state.lock().int_enable = val;
633 if (val & BATTERY_INT_MASK) != 0 && !self.activated {
634 self.start_monitor();
635 }
636 }
637 _ => {
638 warn!("{}: Bad write to address {}", self.debug_label(), info);
639 }
640 };
641 }
642}
643
644impl Aml for GoldfishBattery {
645 fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
646 aml::Device::new(
647 "GFBY".into(),
648 vec![
649 &aml::Name::new("_HID".into(), &"GFSH0001"),
650 &aml::Name::new(
651 "_CRS".into(),
652 &aml::ResourceTemplate::new(vec![
653 &aml::Memory32Fixed::new(true, self.mmio_base, GOLDFISHBAT_MMIO_LEN as u32),
654 &aml::Interrupt::new(true, false, false, true, self.irq_num),
655 ]),
656 ),
657 ],
658 )
659 .to_aml_bytes(bytes);
660 }
661}
662
663impl Suspendable for GoldfishBattery {
664 fn sleep(&mut self) -> anyhow::Result<()> {
665 if let Some(thread) = self.monitor_thread.take() {
666 thread.stop();
667 }
668 Ok(())
669 }
670
671 fn wake(&mut self) -> anyhow::Result<()> {
672 if self.activated {
673 self.activated = false;
675 self.start_monitor();
676 }
677 Ok(())
678 }
679
680 fn snapshot(&mut self) -> anyhow::Result<AnySnapshot> {
681 AnySnapshot::to_any(GoldfishBatterySnapshot {
682 state: self.state.lock().clone(),
683 mmio_base: self.mmio_base,
684 irq_num: self.irq_num,
685 activated: self.activated,
686 })
687 .context("failed to snapshot GoldfishBattery")
688 }
689
690 fn restore(&mut self, data: AnySnapshot) -> anyhow::Result<()> {
691 let deser: GoldfishBatterySnapshot =
692 AnySnapshot::from_any(data).context("failed to deserialize GoldfishBattery")?;
693 {
694 let mut locked_state = self.state.lock();
695 *locked_state = deser.state;
696 }
697 self.mmio_base = deser.mmio_base;
698 self.irq_num = deser.irq_num;
699 self.activated = deser.activated;
700 Ok(())
701 }
702}
703
704#[cfg(test)]
705mod tests {
706 use std::time::SystemTime;
707
708 use super::*;
709 use crate::suspendable_tests;
710
711 fn modify_device(battery: &mut GoldfishBattery) {
712 let mut state = battery.state.lock();
713 state.set_capacity(70);
714 }
715
716 suspendable_tests! {
717 battery, GoldfishBattery::new(
718 0,
719 0,
720 IrqLevelEvent::new().unwrap(),
721 Tube::pair().unwrap().1,
722 None,
723 None,
724 ).unwrap(),
725 modify_device
726 }
727
728 struct MockPowerClient {
730 last_request_time: Option<SystemTime>,
731 }
732
733 impl MockPowerClient {
734 fn new(last_request_time: Option<SystemTime>) -> Self {
735 Self { last_request_time }
736 }
737 }
738
739 impl power_monitor::PowerClient for MockPowerClient {
740 fn last_request_timestamp(&self) -> Option<SystemTime> {
741 self.last_request_time
742 }
743
744 fn get_power_data(
745 &mut self,
746 ) -> std::result::Result<power_monitor::PowerData, Box<dyn std::error::Error>> {
747 self.last_request_time = Some(SystemTime::now());
748 Ok(power_monitor::PowerData {
749 ac_online: true,
750 battery: Some(power_monitor::BatteryData {
751 percent: 50,
752 status: power_monitor::BatteryStatus::Unknown,
753 voltage: 0,
754 current: 0,
755 charge_counter: 0,
756 charge_full: 0,
757 }),
758 })
759 }
760 }
761
762 fn create_mock_power_client(last_request_time: Option<SystemTime>) -> MockPowerClient {
763 MockPowerClient::new(last_request_time)
764 }
765
766 #[test]
767 fn test_initialize_battery_state_rate_limiting() {
768 let mmio_base = 0;
769 let irq_num = 0;
770 let irq_evt = IrqLevelEvent::new().unwrap();
771 let tube = Tube::pair().unwrap().1;
772
773 let now = SystemTime::now();
774 let recent_time =
775 now - Duration::from_millis(GoldfishBattery::POWERD_REQ_INTERVAL_MS - 500);
776
777 let mut battery = GoldfishBattery::new(
778 mmio_base,
779 irq_num,
780 irq_evt,
781 tube,
782 None,
783 Some(Box::new(move || {
784 Ok(Box::new(create_mock_power_client(Some(recent_time))))
785 })),
786 )
787 .unwrap();
788
789 assert!(matches!(battery.initialize_battery_state(), Ok(())));
790
791 assert!(matches!(
793 *battery.init_state.lock(),
794 BatInitializationState::Pending(_)
795 ));
796
797 *battery.init_state.lock() = BatInitializationState::NotYet;
798 let old_time = now - Duration::from_millis(GoldfishBattery::POWERD_REQ_INTERVAL_MS + 500);
799 battery.create_powerd_client = Some(Box::new(move || {
801 Ok(Box::new(create_mock_power_client(Some(old_time))))
802 }));
803
804 assert!(matches!(battery.initialize_battery_state(), Ok(())));
806
807 assert!(matches!(
808 *battery.init_state.lock(),
809 BatInitializationState::Done,
810 ));
811
812 let state = battery.state.lock();
814 assert_eq!(state.ac_online, 1);
815 assert_eq!(state.capacity, 50);
816 }
817
818 #[test]
819 fn test_initialize_battery_state_no_powerd_client() {
820 let mmio_base = 0;
821 let irq_num = 0;
822 let irq_evt = IrqLevelEvent::new().unwrap();
823 let tube = Tube::pair().unwrap().1;
824
825 let mut battery =
826 GoldfishBattery::new(mmio_base, irq_num, irq_evt, tube, None, None).unwrap();
827
828 assert!(matches!(battery.initialize_battery_state(), Ok(())));
829 assert!(matches!(
830 *battery.init_state.lock(),
831 BatInitializationState::NotYet,
832 ));
833 }
834}