summary refs log tree commit diff
path: root/devices/src/acpi.rs
blob: 1cfab3513da3efdadd630aa8ad9a4ef34ccec002 (plain) (blame)
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
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

use crate::{BusDevice, BusResumeDevice};
use acpi_tables::{aml, aml::Aml};
use sys_util::{error, warn, EventFd};

/// ACPI PM resource for handling OS suspend/resume request
pub struct ACPIPMResource {
    suspend_evt: EventFd,
    pm1_status: u16,
    pm1_enable: u16,
    pm1_control: u16,
    sleep_control: u8,
    sleep_status: u8,
}

impl ACPIPMResource {
    /// Constructs ACPI Power Management Resouce.
    pub fn new(suspend_evt: EventFd) -> ACPIPMResource {
        ACPIPMResource {
            suspend_evt,
            pm1_status: 0,
            pm1_enable: 0,
            pm1_control: 0,
            sleep_control: 0,
            sleep_status: 0,
        }
    }
}

/// the ACPI PM register length.
pub const ACPIPM_RESOURCE_LEN: u8 = 8;
pub const ACPIPM_RESOURCE_EVENTBLK_LEN: u8 = 4;
pub const ACPIPM_RESOURCE_CONTROLBLK_LEN: u8 = 2;

/// ACPI PM register value definations
const PM1_STATUS: u16 = 0;
const PM1_ENABLE: u16 = 2;
const PM1_CONTROL: u16 = 4;
const SLEEP_CONTROL: u16 = 6;
const SLEEP_STATUS: u16 = 7;
const BITMASK_PM1CNT_SLEEP_ENABLE: u16 = 0x2000;
const BITMASK_SLEEPCNT_SLEEP_ENABLE: u8 = 0x20;
const BITMASK_PM1CNT_WAKE_STATUS: u16 = 0x8000;
const BITMASK_SLEEPCNT_WAKE_STATUS: u8 = 0x80;

impl BusDevice for ACPIPMResource {
    fn debug_label(&self) -> String {
        "ACPIPMResource".to_owned()
    }

    fn read(&mut self, offset: u64, data: &mut [u8]) {
        let val = match offset as u16 {
            PM1_STATUS => self.pm1_status,
            PM1_ENABLE => self.pm1_enable,
            PM1_CONTROL => self.pm1_control,
            SLEEP_CONTROL => self.sleep_control as u16,
            SLEEP_STATUS => self.sleep_status as u16,
            _ => {
                warn!("ACPIPM: Bad read from offset {}", offset);
                return;
            }
        };

        let val_arr = val.to_ne_bytes();
        for i in 0..std::mem::size_of::<u16>() {
            if i < data.len() {
                data[i] = val_arr[i];
            }
        }
    }

    fn write(&mut self, offset: u64, data: &[u8]) {
        let max_bytes = std::mem::size_of::<u16>();

        // only allow maximum max_bytes to write
        if data.len() > max_bytes {
            warn!("ACPIPM: bad write size: {}", data.len());
            return;
        }

        let mut val_arr = u16::to_ne_bytes(0 as u16);
        for i in 0..std::mem::size_of::<u16>() {
            if i < data.len() {
                val_arr[i] = data[i];
            }
        }
        let val = u16::from_ne_bytes(val_arr);

        match offset as u16 {
            PM1_STATUS => self.pm1_status &= !val,
            PM1_ENABLE => self.pm1_enable = val,
            PM1_CONTROL => {
                if (val & BITMASK_PM1CNT_SLEEP_ENABLE) == BITMASK_PM1CNT_SLEEP_ENABLE {
                    if let Err(e) = self.suspend_evt.write(1) {
                        error!("ACPIPM: failed to trigger suspend event: {}", e);
                    }
                }
                self.pm1_control = val & !BITMASK_PM1CNT_SLEEP_ENABLE;
            }
            SLEEP_CONTROL => {
                let sleep_control = val as u8;
                if (sleep_control & BITMASK_SLEEPCNT_SLEEP_ENABLE) == BITMASK_SLEEPCNT_SLEEP_ENABLE
                {
                    if let Err(e) = self.suspend_evt.write(1) {
                        error!("ACPIPM: failed to trigger suspend event: {}", e);
                    }
                }
                self.sleep_control = sleep_control as u8 & !BITMASK_SLEEPCNT_SLEEP_ENABLE;
            }
            SLEEP_STATUS => self.sleep_status &= !val as u8,
            _ => {
                warn!("ACPIPM: Bad write to offset {}", offset);
            }
        };
    }
}

impl BusResumeDevice for ACPIPMResource {
    fn resume_imminent(&mut self) {
        let val = self.pm1_status;
        self.pm1_status = val | BITMASK_PM1CNT_WAKE_STATUS;

        let val = self.sleep_status;
        self.sleep_status = val | BITMASK_SLEEPCNT_WAKE_STATUS;
    }
}

impl Aml for ACPIPMResource {
    fn to_aml_bytes(&self, bytes: &mut Vec<u8>) {
        // S1
        aml::Name::new(
            "_S1_".into(),
            &aml::Package::new(vec![&aml::ONE, &aml::ONE, &aml::ZERO, &aml::ZERO]),
        )
        .to_aml_bytes(bytes);
    }
}