1
0
mirror of https://github.com/trezor/trezor-firmware.git synced 2025-08-02 11:58:32 +00:00

chore(core/rust): Add IO module and polling function

This commit is contained in:
Jan Pochyla 2022-03-07 10:59:45 -03:00 committed by grdddj
parent 53918e1a4b
commit c0962be091
4 changed files with 123 additions and 30 deletions

89
core/embed/rust/src/io.rs Normal file
View 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);
}
}
}

View File

@ -16,6 +16,7 @@ mod trezorhal;
#[cfg(feature = "ui")] #[cfg(feature = "ui")]
#[macro_use] #[macro_use]
mod ui; mod ui;
mod io;
mod util; mod util;
#[cfg(not(feature = "test"))] #[cfg(not(feature = "test"))]

View File

@ -13,20 +13,6 @@ pub enum UsbError {
InterfaceNotFound, 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 /// Initialize the USB stack with information from `config`, register all
/// present USB interfaces, and start the communication. /// present USB interfaces, and start the communication.
/// ///
@ -60,24 +46,23 @@ pub fn usb_open(mut config: UsbConfig) -> Result<(), UsbError> {
Ok(()) 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. /// Returns immediately, does not block. Useful for constructing event loops.
pub fn usb_poll(ticket: IfaceTicket, interest: Interest) -> Event { pub fn usb_is_ready_to_read(ticket: IfaceTicket) -> bool {
let ready = match ticket { match ticket {
IfaceTicket::Hid(iface_num) => match interest { IfaceTicket::Hid(iface_num) => HidConfig::ready_to_read(iface_num),
Interest::Read => HidConfig::ready_to_read(iface_num), IfaceTicket::WebUsb(iface_num) => WebUsbConfig::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), /// Check if interface `ticket` is ready to write a report.
Interest::Write => WebUsbConfig::ready_to_write(iface_num), ///
}, /// Returns immediately, does not block. Useful for constructing event loops.
}; pub fn usb_is_ready_to_write(ticket: IfaceTicket) -> bool {
if ready { match ticket {
Event::Ready IfaceTicket::Hid(iface_num) => HidConfig::ready_to_write(iface_num),
} else { IfaceTicket::WebUsb(iface_num) => WebUsbConfig::ready_to_write(iface_num),
Event::Pending
} }
} }

View File

@ -1,5 +1,7 @@
use core::slice; use core::slice;
use crate::micropython::time;
use crate::time::Duration;
use crate::{ use crate::{
error::Error, error::Error,
micropython::{ 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. /// Perform a call and convert errors into a raised MicroPython exception.
/// Should only called when returning from Rust to C. See `raise_exception` for /// Should only called when returning from Rust to C. See `raise_exception` for
/// details. /// details.