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
use core::ffi::{c_void, CStr};
use crate::{NamespaceNode, Status};

extern crate alloc;

use alloc::vec::Vec;
use core::slice;

#[repr(C)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)]
pub enum NsIterDecision {
	Continue,
	NextPeer,
	Break
}

#[repr(C)]
#[derive(Ord, PartialOrd, Eq, PartialEq, Copy, Clone, Debug)]
pub enum InterruptModel {
	Pic,
	IoApic,
	IoSapic
}

#[repr(transparent)]
pub struct IdString(pub(crate) *mut uacpi_sys::uacpi_id_string);

impl Drop for IdString {
	fn drop(&mut self) {
		unsafe {
			uacpi_sys::uacpi_free_id_string(self.0);
		}
	}
}

impl IdString {
	pub fn as_str(&self) -> &str {
		unsafe {
			let slice = slice::from_raw_parts(
				(*self.0).value as *const u8,
				(*self.0).size as usize
			);
			CStr::from_bytes_with_nul(slice).unwrap()
		}.to_str().unwrap()
	}
}

/// Checks whether the device at `node` matches any of the PNP ids provided in `list`.
/// This is done by first attempting to match the value returned from _HID
/// and then the value(s) from _CID.
/// Note that the presence of the device (_STA) is not verified here.
pub fn device_matches_pnp_id(node: &NamespaceNode, list: &[&CStr]) -> bool {
	let mut vec: Vec<_> = list.iter().map(|str| str.as_ptr()).collect();
	vec.push(core::ptr::null());
	unsafe {
		uacpi_sys::uacpi_device_matches_pnp_id(node.0, vec.as_ptr())
	}
}

unsafe extern "C" fn uacpi_iter_cb<F: FnMut(&NamespaceNode) -> NsIterDecision>(
	user: *mut c_void,
	node: *mut uacpi_sys::uacpi_namespace_node
) -> uacpi_sys::uacpi_ns_iteration_decision {
	let f = user as *mut F;
	(*f)(*(node as *mut _)) as _
}

/// Finds all the devices in the namespace starting at `parent` matching the
/// specified `hids`. Only devices reported as present via _STA are checked.
/// Any matching devices are then passed to the `cb`.
pub fn find_devices_at<F: FnMut(&NamespaceNode) -> NsIterDecision>(
	parent: &NamespaceNode,
	hids: &[&CStr],
	cb: F
) -> Result<(), Status> {
	let mut vec: Vec<_> = hids.iter().map(|str| str.as_ptr()).collect();
	vec.push(core::ptr::null());
	let status: Status = unsafe {
		uacpi_sys::uacpi_find_devices_at(
			parent.0,
			vec.as_ptr(),
			Some(uacpi_iter_cb::<F>),
			&cb as *const _ as _
		).into()
	};

	match status {
		Status::Ok => Ok(()),
		_ => Err(status)
	}
}

/// Same as find_devices_at, except this starts at the root and only
/// matches one hid.
pub fn find_devices<F: FnMut(&NamespaceNode) -> NsIterDecision>(
	hid: &CStr,
	cb: F
) -> Result<(), Status> {
	let status: Status = unsafe {
		uacpi_sys::uacpi_find_devices(
			hid.as_ptr(),
			Some(uacpi_iter_cb::<F>),
			&cb as *const _ as _
		).into()
	};

	match status {
		Status::Ok => Ok(()),
		_ => Err(status)
	}
}

/// Sets the currently active interrupt model.
pub fn set_interrupt_model(model: InterruptModel) -> Result<(), Status> {
	let status: Status = unsafe {
		uacpi_sys::uacpi_set_interrupt_model(
			model as _
		).into()
	};

	match status {
		Status::Ok => Ok(()),
		_ => Err(status)
	}
}

/// Evaluate a device's _HID method and get its value.
pub fn eval_hid(node: &NamespaceNode) -> Result<IdString, Status> {
	let mut ret = core::ptr::null_mut();
	let status: Status = unsafe {
		uacpi_sys::uacpi_eval_hid(
			node.0,
			&mut ret
		).into()
	};

	match status {
		Status::Ok => Ok(IdString(ret)),
		_ => Err(status)
	}
}