mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-08-01 19:38:33 +00:00
chore(core/rust): Add IO module and polling function
This commit is contained in:
parent
53918e1a4b
commit
c0962be091
89
core/embed/rust/src/io.rs
Normal file
89
core/embed/rust/src/io.rs
Normal file
@ -0,0 +1,89 @@
|
||||
use crate::{
|
||||
error::Error,
|
||||
time::{Duration, Instant},
|
||||
trezorhal::usb::{usb_is_ready_to_read, usb_is_ready_to_write, IfaceTicket},
|
||||
util::wait_in_busy_loop,
|
||||
};
|
||||
|
||||
mod ffi {
|
||||
extern "C" {
|
||||
#[cfg(feature = "model_t1")]
|
||||
pub fn button_read() -> u32;
|
||||
#[cfg(feature = "model_tt")]
|
||||
pub fn touch_read() -> u32;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "model_t1")]
|
||||
pub fn button_read() -> u32 {
|
||||
unsafe { ffi::button_read() }
|
||||
}
|
||||
|
||||
#[cfg(feature = "model_tt")]
|
||||
pub fn touch_read() -> u32 {
|
||||
unsafe { ffi::touch_read() }
|
||||
}
|
||||
|
||||
pub enum Resource {
|
||||
#[cfg(feature = "model_t1")]
|
||||
Button,
|
||||
#[cfg(feature = "model_tt")]
|
||||
Touch,
|
||||
UsbRead(IfaceTicket),
|
||||
UsbWrite(IfaceTicket),
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
#[cfg(feature = "model_t1")]
|
||||
Button(u32), // TODO: We should somehow use `ButtonEvent` from UI here.
|
||||
#[cfg(feature = "model_tt")]
|
||||
Touch(u32), // TODO: We should somehow use `TouchEvent` from UI here.
|
||||
UsbRead(IfaceTicket),
|
||||
UsbWrite(IfaceTicket),
|
||||
TimedOut,
|
||||
}
|
||||
|
||||
pub fn poll_io(resources: &[Resource], timeout: Duration) -> Result<Event, Error> {
|
||||
let deadline = Instant::now()
|
||||
.checked_add(timeout)
|
||||
.ok_or(Error::OutOfRange)?;
|
||||
|
||||
loop {
|
||||
// Poll all resources, return if ready.
|
||||
for &resource in resources {
|
||||
match resource {
|
||||
#[cfg(feature = "model_t1")]
|
||||
Resource::Button => {
|
||||
let event = button_read();
|
||||
if event != 0 {
|
||||
return Ok(Event::Button(event));
|
||||
}
|
||||
}
|
||||
#[cfg(feature = "model_tt")]
|
||||
Resource::Touch => {
|
||||
let event = touch_read();
|
||||
if event != 0 {
|
||||
return Ok(Event::Touch(event));
|
||||
}
|
||||
}
|
||||
Resource::UsbRead(ticket) => {
|
||||
if usb_is_ready_to_read(ticket) {
|
||||
return Ok(Event::UsbRead(ticket));
|
||||
}
|
||||
}
|
||||
Resource::UsbWrite(ticket) => {
|
||||
if usb_is_ready_to_write(ticket) {
|
||||
return Ok(Event::UsbWrite(ticket));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// No resource is ready yet, wait or exit on timeout.
|
||||
if Instant::now() < deadline {
|
||||
wait_in_busy_loop();
|
||||
} else {
|
||||
return Ok(Event::TimedOut);
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,7 @@ mod trezorhal;
|
||||
#[cfg(feature = "ui")]
|
||||
#[macro_use]
|
||||
mod ui;
|
||||
mod io;
|
||||
mod util;
|
||||
|
||||
#[cfg(not(feature = "test"))]
|
||||
|
@ -13,20 +13,6 @@ pub enum UsbError {
|
||||
InterfaceNotFound,
|
||||
}
|
||||
|
||||
pub enum Interest {
|
||||
/// Caller is interested to read bytes/messages.
|
||||
Read,
|
||||
/// Caller is interested to write bytes/messages.
|
||||
Write,
|
||||
}
|
||||
|
||||
pub enum Event {
|
||||
/// Resource is ready to perform on selected `Interest`.
|
||||
Ready,
|
||||
/// Resource is not yet ready. No side-effect was performed.
|
||||
Pending,
|
||||
}
|
||||
|
||||
/// Initialize the USB stack with information from `config`, register all
|
||||
/// present USB interfaces, and start the communication.
|
||||
///
|
||||
@ -60,24 +46,23 @@ pub fn usb_open(mut config: UsbConfig) -> Result<(), UsbError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check if interface `ticket` is ready to perform on selected `interest`.
|
||||
/// Check if interface `ticket` is ready to read a report.
|
||||
///
|
||||
/// Returns immediately, does not block. Useful for constructing event loops.
|
||||
pub fn usb_poll(ticket: IfaceTicket, interest: Interest) -> Event {
|
||||
let ready = match ticket {
|
||||
IfaceTicket::Hid(iface_num) => match interest {
|
||||
Interest::Read => HidConfig::ready_to_read(iface_num),
|
||||
Interest::Write => HidConfig::ready_to_write(iface_num),
|
||||
},
|
||||
IfaceTicket::WebUsb(iface_num) => match interest {
|
||||
Interest::Read => WebUsbConfig::ready_to_read(iface_num),
|
||||
Interest::Write => WebUsbConfig::ready_to_write(iface_num),
|
||||
},
|
||||
};
|
||||
if ready {
|
||||
Event::Ready
|
||||
} else {
|
||||
Event::Pending
|
||||
pub fn usb_is_ready_to_read(ticket: IfaceTicket) -> bool {
|
||||
match ticket {
|
||||
IfaceTicket::Hid(iface_num) => HidConfig::ready_to_read(iface_num),
|
||||
IfaceTicket::WebUsb(iface_num) => WebUsbConfig::ready_to_read(iface_num),
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if interface `ticket` is ready to write a report.
|
||||
///
|
||||
/// Returns immediately, does not block. Useful for constructing event loops.
|
||||
pub fn usb_is_ready_to_write(ticket: IfaceTicket) -> bool {
|
||||
match ticket {
|
||||
IfaceTicket::Hid(iface_num) => HidConfig::ready_to_write(iface_num),
|
||||
IfaceTicket::WebUsb(iface_num) => WebUsbConfig::ready_to_write(iface_num),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
use core::slice;
|
||||
|
||||
use crate::micropython::time;
|
||||
use crate::time::Duration;
|
||||
use crate::{
|
||||
error::Error,
|
||||
micropython::{
|
||||
@ -9,6 +11,22 @@ use crate::{
|
||||
},
|
||||
};
|
||||
|
||||
/// Wait an unspecified short amount of time. To be used in busy loops.
|
||||
pub fn wait_in_busy_loop() {
|
||||
match () {
|
||||
#[cfg(cortex_m)]
|
||||
() => {
|
||||
// In device context, run the WFI instruction.
|
||||
unsafe { asm!("wfi" :::: "volatile") }
|
||||
}
|
||||
#[cfg(not(cortex_m))]
|
||||
() => {
|
||||
// In desktop context, we sleep for 1ms.
|
||||
time::sleep(Duration::from_millis(1))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a call and convert errors into a raised MicroPython exception.
|
||||
/// Should only called when returning from Rust to C. See `raise_exception` for
|
||||
/// details.
|
||||
|
Loading…
Reference in New Issue
Block a user