From c0962be091431ff4cb295284eff315d12abb2b86 Mon Sep 17 00:00:00 2001 From: Jan Pochyla Date: Mon, 7 Mar 2022 10:59:45 -0300 Subject: [PATCH] chore(core/rust): Add IO module and polling function --- core/embed/rust/src/io.rs | 89 ++++++++++++++++++++++++++++ core/embed/rust/src/lib.rs | 1 + core/embed/rust/src/trezorhal/usb.rs | 45 +++++--------- core/embed/rust/src/util.rs | 18 ++++++ 4 files changed, 123 insertions(+), 30 deletions(-) create mode 100644 core/embed/rust/src/io.rs diff --git a/core/embed/rust/src/io.rs b/core/embed/rust/src/io.rs new file mode 100644 index 0000000000..2a8d150418 --- /dev/null +++ b/core/embed/rust/src/io.rs @@ -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 { + 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); + } + } +} diff --git a/core/embed/rust/src/lib.rs b/core/embed/rust/src/lib.rs index ec86783907..bb395da97f 100644 --- a/core/embed/rust/src/lib.rs +++ b/core/embed/rust/src/lib.rs @@ -16,6 +16,7 @@ mod trezorhal; #[cfg(feature = "ui")] #[macro_use] mod ui; +mod io; mod util; #[cfg(not(feature = "test"))] diff --git a/core/embed/rust/src/trezorhal/usb.rs b/core/embed/rust/src/trezorhal/usb.rs index b7667c2b64..97514379a9 100644 --- a/core/embed/rust/src/trezorhal/usb.rs +++ b/core/embed/rust/src/trezorhal/usb.rs @@ -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), } } diff --git a/core/embed/rust/src/util.rs b/core/embed/rust/src/util.rs index 885b72220a..39abd7ba05 100644 --- a/core/embed/rust/src/util.rs +++ b/core/embed/rust/src/util.rs @@ -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.