acpica_bindings/interface/
mod.rs

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
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
use core::ops::{Deref, DerefMut};

use alloc::{boxed::Box, ffi::CString, vec::Vec};
use spin::Mutex;

use crate::bindings::{
    consts::ACPI_FULL_INITIALIZATION,
    functions::{
        AcpiEnableSubsystem, AcpiInitializeObjects, AcpiInitializeSubsystem, AcpiInitializeTables,
        AcpiLoadTables,
    },
};

use self::{handler::AcpiHandler, status::AcpiError};

pub mod handler;

pub mod devices;
pub mod status;
mod tables;
pub mod types;

static OS_INTERFACE: Mutex<Option<OsInterface>> = Mutex::new(None);

#[derive(Debug)]
enum DropOnTerminate {
    CString(CString),
}

struct OsInterface {
    handler: Box<dyn AcpiHandler + Send>,
    objects_to_drop: Vec<DropOnTerminate>,
}

impl Deref for OsInterface {
    type Target = Box<dyn AcpiHandler + Send>;

    fn deref(&self) -> &Self::Target {
        &self.handler
    }
}

impl DerefMut for OsInterface {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.handler
    }
}

/// Registers `interface` as the handler for ACPICA functions, and starts the initialization of ACPICA.
/// See the docs for [`AcpicaOperation`] for more info.
///
/// # Panics
/// If called more than once.
pub fn register_interface<T: AcpiHandler + Send + 'static>(
    interface: T,
) -> Result<AcpicaOperation<false, false, false, false>, AcpiError> {
    let mut lock = OS_INTERFACE.lock();

    assert!(!lock.is_some(), "Interface is already initialized");

    *lock = Some(OsInterface {
        handler: Box::new(interface),
        objects_to_drop: Vec::new(),
    });

    // AcpiInitializeSubsystem calls functions which need this lock
    drop(lock);

    // SAFETY: Handlers for AcpiOs functions have been set up
    unsafe { AcpiInitializeSubsystem().as_result()? };

    Ok(AcpicaOperation)
}

/// The interface to ACPICA functions. The state of ACPICA's initialization is tracked using const generics on this type.
///
/// ACPICA is not initialized all at once - it has multiple initialization functions for different systems.
/// This struct keeps track of the calling of these functions using the type parameters `T`, for whether ACPICA tables are initialized,
/// and `S` for whether the ACPICA subsystem is enabled. An `AcpiOperation<false, false>` can be obtained from [`register_interface`],
/// which also calls `AcpiInitializeSubsystem`.
///
/// Subsystem initialization code could look like the following:
///
/// ```ignore
/// # use acpica_bindings::status::AcpiError;
/// # use acpica_bindings::handler::register_interface;
/// # fn main() -> Result<(), AcpiError> {
///     let interface = todo!(); // In real code this would be an object implementing the AcpiHandler trait
///
///     let initialization = register_interface(interface)?;
///     let initialization = initialization.load_tables()?;
///     let initialization = initialization.enable_subsystem()?;
///     let initialization = initialization.initialize_objects()?;
///
/// #   Ok(())
/// # }
/// ```
#[derive(Debug)]
#[must_use]
pub struct AcpicaOperation<
    const TABLES_INITIALIZED: bool,
    const TABLES_LOADED: bool,
    const SUBSYSTEM_ENABLED: bool,
    const OBJECTS_INITIALIZED: bool,
>;

/// An alias to an [`AcpicaOperation`] which is completely initialized, and all methods are available.
pub type AcpicaOperationFullyInitialized = AcpicaOperation<true, true, true, true>;

impl AcpicaOperation<false, false, false, false> {
    /// Calls the ACPICA function `AcpiInitializeTables`.
    ///
    /// This function causes ACPICA to parse all the tables pointed to by the RSDT/XSDT
    pub fn initialize_tables(
        self,
    ) -> Result<AcpicaOperation<true, false, false, false>, AcpiError> {
        // SAFETY: `AcpiInitializeSubsystem` has been called
        unsafe { AcpiInitializeTables(core::ptr::null_mut(), 16, false).as_result()? };

        Ok(AcpicaOperation)
    }
}

impl AcpicaOperation<true, false, false, false> {
    /// Calls the ACPICA function `AcpiLoadTables`.
    ///
    /// This function causes ACPICA to parse and execute AML code in order to build the AML namespace.
    pub fn load_tables(self) -> Result<AcpicaOperation<true, true, false, false>, AcpiError> {
        // SAFETY: `AcpiInitializeTables` has been called
        unsafe { AcpiLoadTables().as_result()? };

        Ok(AcpicaOperation)
    }
}

impl AcpicaOperation<true, true, false, false> {
    /// Calls the ACPICA function `AcpiEnableSubsystem`.
    ///
    /// This function causes ACPICA to enter ACPI mode and start receiving ACPI interrupts.
    pub fn enable_subsystem(self) -> Result<AcpicaOperation<true, true, true, false>, AcpiError> {
        // SAFETY: `AcpiLoadTables` has been called
        unsafe { AcpiEnableSubsystem(ACPI_FULL_INITIALIZATION).as_result()? };

        Ok(AcpicaOperation)
    }
}

impl AcpicaOperation<true, true, true, false> {
    /// Calls the ACPICA function `AcpiEnableSubsystem`.
    ///
    /// This function causes ACPICA to enter ACPI mode and start receiving ACPI interrupts.
    pub fn initialize_objects(self) -> Result<AcpicaOperationFullyInitialized, AcpiError> {
        // SAFETY: `AcpiEnableSubsystem` has been called
        unsafe { AcpiInitializeObjects(ACPI_FULL_INITIALIZATION).as_result()? };

        Ok(AcpicaOperation)
    }
}