Skip to content

Commit c3a2b2f

Browse files
authored
Event support and general console protocol cleanup (#44)
- Implemented basic support for awaiting UEFI events. - Made Handle and Event different newtypes of `void*` so assigning one to the other is an error * No ergonomics are lost since from the user's point of view these are opaque types anyway. - Clarified the stateful nature of Pointer::get_state - Made text input protocol more consistent with pointer protocol - Improved documentation and method ordering of text output protocol
1 parent 7907a68 commit c3a2b2f

File tree

7 files changed

+119
-44
lines changed

7 files changed

+119
-44
lines changed

src/data_types/mod.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
1-
/// A pointer to an opaque data structure.
2-
pub type Handle = *mut ();
1+
use core::ffi::c_void;
2+
3+
/// Opaque handle to an UEFI entity (protocol, image...)
4+
#[derive(Clone, Copy)]
5+
#[repr(transparent)]
6+
pub struct Handle(*mut c_void);
7+
8+
/// Handle to an event structure
9+
#[derive(Clone, Copy)]
10+
#[repr(transparent)]
11+
pub struct Event(*mut c_void);
312

413
mod guid;
514
pub use self::guid::Guid;

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ mod error;
3636
pub use self::error::{Result, Status};
3737

3838
mod data_types;
39-
pub use self::data_types::{Guid, Handle};
39+
pub use self::data_types::{Event, Guid, Handle};
4040

4141
pub mod table;
4242

src/proto/console/pointer/mod.rs

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
//! Pointer device access.
22
33
use core::mem;
4-
use crate::{Result, Status};
4+
use crate::{Event, Result, Status};
55

66
/// Provides information about a pointer device.
77
#[repr(C)]
88
pub struct Pointer {
99
reset: extern "win64" fn(this: &mut Pointer, ext_verif: bool) -> Status,
1010
get_state: extern "win64" fn(this: &Pointer, state: &mut PointerState) -> Status,
11-
_wait_for_input: usize,
11+
wait_for_input: Event,
1212
mode: &'static PointerMode,
1313
}
1414

@@ -25,13 +25,15 @@ impl Pointer {
2525
(self.reset)(self, extended_verification).into()
2626
}
2727

28-
/// Retrieves the pointer device's current state.
28+
/// Retrieves the pointer device's current state, if a state change occured
29+
/// since the last time this function was called.
2930
///
30-
/// Will return None if the state has not changed since the last query.
31+
/// Use wait_for_input_event() with the BootServices::wait_for_event()
32+
/// interface in order to wait for input from the pointer device.
3133
///
3234
/// # Errors
3335
/// - `DeviceError` if there was an issue with the pointer device.
34-
pub fn state(&self) -> Result<Option<PointerState>> {
36+
pub fn read_state(&mut self) -> Result<Option<PointerState>> {
3537
let mut pointer_state = unsafe { mem::uninitialized() };
3638

3739
match (self.get_state)(self, &mut pointer_state) {
@@ -41,6 +43,12 @@ impl Pointer {
4143
}
4244
}
4345

46+
/// Event to use with BootServices::wait_for_event() to wait for input from
47+
/// the pointer device
48+
pub fn wait_for_input_event(&self) -> Event {
49+
self.wait_for_input
50+
}
51+
4452
/// Returns a reference to the pointer device information.
4553
pub fn mode(&self) -> &PointerMode {
4654
self.mode

src/proto/console/text/input.rs

Lines changed: 29 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,50 @@
11
use core::mem;
2-
use crate::{Result, Status};
2+
use crate::{Event, Result, Status};
33

44
/// Interface for text-based input devices.
55
#[repr(C)]
66
pub struct Input {
77
reset: extern "win64" fn(this: &mut Input, extended: bool) -> Status,
88
read_key_stroke: extern "win64" fn(this: &mut Input, key: &mut Key) -> Status,
9+
wait_for_key: Event,
910
}
1011

1112
impl Input {
1213
/// Resets the input device hardware.
13-
pub fn reset(&mut self, extended: bool) -> Result<()> {
14-
(self.reset)(self, extended).into()
14+
///
15+
/// The `extended_verification` parameter is used to request that UEFI
16+
/// performs an extended check and reset of the input device.
17+
///
18+
/// # Errors
19+
///
20+
/// - `DeviceError` if the device is malfunctioning and cannot be reset.
21+
pub fn reset(&mut self, extended_verification: bool) -> Result<()> {
22+
(self.reset)(self, extended_verification).into()
1523
}
1624

17-
/// Reads the next keystroke from the input device.
25+
/// Reads the next keystroke from the input device, if any.
26+
///
27+
/// Use wait_for_key_event() with the BootServices::wait_for_event()
28+
/// interface in order to wait for a key to be pressed.
1829
///
19-
/// Returns `Err(NotReady)` if no keystroke is available yet.
20-
pub fn read_key(&mut self) -> Result<Key> {
30+
/// # Errors
31+
///
32+
/// - `DeviceError` if there was an issue with the input device
33+
pub fn read_key(&mut self) -> Result<Option<Key>> {
2134
let mut key = unsafe { mem::uninitialized() };
22-
(self.read_key_stroke)(self, &mut key)?;
23-
Ok(key)
24-
}
2535

26-
/// Blocks until a key is read from the device or an error occurs.
27-
pub fn read_key_sync(&mut self) -> Result<Key> {
28-
loop {
29-
match self.read_key() {
30-
// Received a key, exit loop.
31-
Ok(key) => return Ok(key),
32-
Err(code) => {
33-
match code {
34-
// Wait for key press.
35-
Status::NotReady => (),
36-
// Exit on error, no point in looping.
37-
_ => return Err(code),
38-
}
39-
}
40-
}
36+
match (self.read_key_stroke)(self, &mut key) {
37+
Status::Success => Ok(Some(key)),
38+
Status::NotReady => Ok(None),
39+
error => Err(error),
4140
}
4241
}
42+
43+
/// Event to use with BootServices::wait_for_event() to wait for a key to be
44+
/// available
45+
pub fn wait_for_key_event(&self) -> Event {
46+
self.wait_for_key
47+
}
4348
}
4449

4550
/// A key read from the console.

src/proto/console/text/output.rs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,16 @@ impl Output {
6262
}
6363
}
6464

65-
/// Returns the width (column count) and height (row count) of this mode.
65+
/// Returns the width (column count) and height (row count) of a text mode.
66+
///
67+
/// Devices are required to support at least an 80x25 text mode and to
68+
/// assign index 0 to it. If 80x50 is supported, then it will be mode 1,
69+
/// otherwise querying for mode 1 will return the `Unsupported` error.
70+
/// Modes 2+ will describe other text modes supported by the device.
71+
///
72+
/// If you want to iterate over all text modes supported by the device,
73+
/// consider using the iterator produced by `modes()` as a more ergonomic
74+
/// alternative to this method.
6675
fn query_mode(&self, index: i32) -> Result<(usize, usize)> {
6776
let (mut columns, mut rows) = (0, 0);
6877
(self.query_mode)(self, index, &mut columns, &mut rows)?;
@@ -81,23 +90,26 @@ impl Output {
8190
Ok(OutputMode { index, dims })
8291
}
8392

84-
/// Enables or disables the cursor.
93+
/// Make the cursor visible or invisible.
94+
///
95+
/// The output device may not support this operation, in which case an
96+
/// `Unsupported` error will be returned.
8597
pub fn enable_cursor(&mut self, visible: bool) -> Result<()> {
8698
(self.enable_cursor)(self, visible).into()
8799
}
88100

101+
/// Returns whether the cursor is currently shown or not.
102+
pub fn cursor_visible(&self) -> bool {
103+
self.data.cursor_visible
104+
}
105+
89106
/// Returns the column and row of the cursor.
90107
pub fn get_cursor_position(&self) -> (usize, usize) {
91108
let column = self.data.cursor_column;
92109
let row = self.data.cursor_row;
93110
(column as usize, row as usize)
94111
}
95112

96-
/// Returns whether the cursor is currently shown or not.
97-
pub fn is_cursor_visible(&self) -> bool {
98-
self.data.cursor_visible
99-
}
100-
101113
/// Sets the cursor's position, relative to the top-left corner, which is (0, 0).
102114
///
103115
/// This function will fail if the cursor's new position would exceed the screen's bounds.
@@ -255,7 +267,7 @@ impl_proto! {
255267
#[derive(Debug, Copy, Clone)]
256268
#[repr(u8)]
257269
pub enum Color {
258-
Black,
270+
Black = 0,
259271
Blue,
260272
Green,
261273
Cyan,

src/table/boot.rs

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
33
use super::Header;
44
use bitflags::bitflags;
5-
use core::{mem, ptr};
5+
use core::{mem, ptr, result};
66
use crate::proto::Protocol;
7-
use crate::{Guid, Handle, Result, Status};
7+
use crate::{Event, Guid, Handle, Result, Status};
88

99
/// Contains pointers to all of the boot services.
1010
#[repr(C)]
@@ -29,7 +29,9 @@ pub struct BootServices {
2929
// Event & timer functions
3030
create_event: usize,
3131
set_timer: usize,
32-
wait_for_event: usize,
32+
wait_for_event:
33+
extern "win64" fn(number_of_events: usize, events: *mut Event, out_index: &mut usize)
34+
-> Status,
3335
signal_event: usize,
3436
close_event: usize,
3537
check_event: usize,
@@ -203,6 +205,43 @@ impl BootServices {
203205
(self.free_pool)(addr).into()
204206
}
205207

208+
/// Stops execution until an event is signaled
209+
///
210+
/// This function must be called at priority level Tpl::Application. If an
211+
/// attempt is made to call it at any other priority level, an `Unsupported`
212+
/// error is returned.
213+
///
214+
/// The input Event slice is repeatedly iterated from first to last until an
215+
/// event is signaled or an error is detected. The following checks are
216+
/// performed on each event:
217+
///
218+
/// * If an event is of type NotifySignal, then an `InvalidParameter` error
219+
/// is returned together with the index of the event that caused the failure.
220+
/// * If an event is in the signaled state, the signaled state is cleared
221+
/// and the index of the event that was signaled is returned.
222+
/// * If an event is not in the signaled state but does have a notification
223+
/// function, the notification function is queued at the event's
224+
/// notification task priority level. If the execution of the event's
225+
/// notification function causes the event to be signaled, then the
226+
/// signaled state is cleared and the index of the event that was signaled
227+
/// is returned.
228+
///
229+
/// To wait for a specified time, a timer event must be included in the
230+
/// Event slice.
231+
///
232+
/// To check if an event is signaled without waiting, an already signaled
233+
/// event can be used as the last event in the slice being checked, or the
234+
/// check_event() interface may be used.
235+
pub fn wait_for_event(&self, events: &mut [Event]) -> result::Result<usize, (Status, usize)> {
236+
let (number_of_events, events) = (events.len(), events.as_mut_ptr());
237+
let mut index = unsafe { mem::uninitialized() };
238+
match (self.wait_for_event)(number_of_events, events, &mut index) {
239+
Status::Success => Ok(index),
240+
s @ Status::InvalidParameter => Err((s, index)),
241+
error => Err((error, 0)),
242+
}
243+
}
244+
206245
/// Query a handle for a certain protocol.
207246
///
208247
/// This function attempts to get the protocol implementation of a handle,

uefi-test-runner/src/proto/console/pointer.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ pub fn test(bt: &BootServices) {
1212
.reset(false)
1313
.expect("Failed to reset pointer device");
1414

15-
let state = pointer.state().expect("Failed to retrieve pointer state");
15+
let state = pointer
16+
.read_state()
17+
.expect("Failed to retrieve pointer state");
1618
if let Some(state) = state {
1719
info!("New pointer State: {:#?}", state);
1820
} else {

0 commit comments

Comments
 (0)