From 57a4c47d73a9758e27e96cd6584b1bd0b8400b29 Mon Sep 17 00:00:00 2001 From: Surya Midatala Date: Sat, 27 Jul 2024 19:10:44 +0000 Subject: [PATCH] Address comments --- crates/board/src/usb.rs | 3 +- crates/board/src/usb/ctap.rs | 6 +- crates/prelude/Cargo.toml | 1 + crates/prelude/src/ctap.rs | 611 +++++++++++++++++------------------ crates/prelude/src/usb.rs | 2 +- 5 files changed, 310 insertions(+), 313 deletions(-) diff --git a/crates/board/src/usb.rs b/crates/board/src/usb.rs index 087abf31..7c1519c2 100644 --- a/crates/board/src/usb.rs +++ b/crates/board/src/usb.rs @@ -17,8 +17,7 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; -// TODO: Should this also be gated behind api-usb-serial feature or should it be -// a different one? +#[cfg(feature = "api-usb-ctap")] pub mod ctap; /// USB event. diff --git a/crates/board/src/usb/ctap.rs b/crates/board/src/usb/ctap.rs index b7a79a52..00bca282 100644 --- a/crates/board/src/usb/ctap.rs +++ b/crates/board/src/usb/ctap.rs @@ -43,12 +43,12 @@ pub trait Api: Send { /// Reads from the USB serial into a buffer. /// /// Returns the number of bytes read. It could be zero if there's nothing to read. - fn read(output: &mut [u8]) -> Result; + fn read(output: &mut [u8; 64]) -> Result; /// Writes from a buffer to the USB serial. /// /// Returns the number of bytes written. It could be zero if the other side is not ready. - fn write(input: &[u8]) -> Result; + fn write(input: &[u8; 64]) -> Result; } pub trait HasCtapHid: Send { @@ -60,7 +60,7 @@ pub trait HasCtapHid: Send { /// Wrapper type for boards using the `usbd_ctaphid` crate. pub struct WithCtapHid { _never: !, - _has_serial: T, + _has_ctap: T, } /// Helper struct for boards using the `usbd_ctaphid` crate. diff --git a/crates/prelude/Cargo.toml b/crates/prelude/Cargo.toml index 9aef9bce..29cab407 100644 --- a/crates/prelude/Cargo.toml +++ b/crates/prelude/Cargo.toml @@ -61,6 +61,7 @@ api-store = ["internal-api-store", "wasefire-applet-api/api-store"] api-store-fragment = ["internal-api-store", "wasefire-applet-api/api-store-fragment"] api-timer = ["wasefire-applet-api/api-timer"] api-uart = ["internal-serial", "wasefire-applet-api/api-uart"] +api-usb-ctap = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-ctap"] api-usb-serial = ["internal-api-usb", "internal-serial", "wasefire-applet-api/api-usb-serial"] # Enables all API features. full-api = [ diff --git a/crates/prelude/src/ctap.rs b/crates/prelude/src/ctap.rs index fc0d7224..70d33653 100644 --- a/crates/prelude/src/ctap.rs +++ b/crates/prelude/src/ctap.rs @@ -14,15 +14,14 @@ //! Provides an API for CTAP HID interface. -// use alloc::boxed::Box; -// use alloc::vec::Vec; -// use core::cell::Cell; +use alloc::boxed::Box; +use alloc::vec::Vec; +use core::cell::Cell; use core::fmt::Debug; use sealed::sealed; -use crate::Error; -// use crate::scheduling; +use crate::{scheduling, Error}; /// CtapHid events to be notified. #[derive(Debug, Copy, Clone, PartialEq, Eq)] @@ -34,8 +33,6 @@ pub enum Event { Write, } -// TODO: What else do we need in the Ctap Api? - /// Provides high-level CtapHid API from low-level API. /// /// This trait should only be implemented by the prelude and is thus sealed. Its purpose is to @@ -46,29 +43,29 @@ pub trait CtapHid { /// /// Returns how many bytes were read (and thus written to the buffer). This function does not /// block, so if there are no data available for read, zero is returned. - fn read(&self, buffer: &mut [u8]) -> Result; + fn read(&self, buffer: &mut [u8; 64]) -> Result; /// Writes from a buffer to the CtapHid. /// /// Returns how many bytes were written (and thus read from the buffer). This function does not /// block, so if the CtapHid is not ready for write, zero is returned. - fn write(&self, buffer: &[u8]) -> Result; - - // /// Flushes the CtapHid (in case reads or writes are buffered). - // fn flush(&self) -> Result<(), Error>; - - // /// Registers a callback for an event. - // /// - // /// # Safety - // /// - // /// The function pointer and data must live until unregistered. The function must support - // /// concurrent calls. - // unsafe fn register( - // &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, - // ) -> Result<(), Error>; - - // /// Unregisters the callback for an event. - // fn unregister(&self, event: Event) -> Result<(), Error>; + fn write(&self, buffer: &[u8; 64]) -> Result; + + /// Flushes the CtapHid (in case reads or writes are buffered). + fn flush(&self) -> Result<(), Error>; + + /// Registers a callback for an event. + /// + /// # Safety + /// + /// The function pointer and data must live until unregistered. The function must support + /// concurrent calls. + unsafe fn register( + &self, event: Event, func: extern "C" fn(*const u8), data: *const u8, + ) -> Result<(), Error>; + + /// Unregisters the callback for an event. + fn unregister(&self, event: Event) -> Result<(), Error>; } /// Reads from the CtapHid into a buffer without blocking. @@ -79,31 +76,31 @@ pub fn read(ctap_hid: &T, buffer: &mut [u8]) -> Result ctap_hid.read(buffer) } -// /// Synchronously reads at least one byte from a CtapHid into a buffer. -// /// -// /// This function will block if necessary. -// pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { -// let mut reader = Reader::new(ctap_hid, buffer); -// scheduling::wait_until(|| !reader.is_empty()); -// reader.result() -// } - -// /// Synchronously reads from a CtapHid into a buffer until it is filled. -// /// -// /// This function will block if necessary. -// pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { -// let mut reader = Reader::new(ctap_hid, buffer); -// scheduling::wait_until(|| reader.is_done()); -// reader.result()?; -// Ok(()) -// } - -// /// Synchronously reads exactly one byte. -// pub fn read_byte(ctap_hid: &T) -> Result { -// let mut byte = 0; -// read_any(ctap_hid, core::slice::from_mut(&mut byte))?; -// Ok(byte) -// } +/// Synchronously reads at least one byte from a CtapHid into a buffer. +/// +/// This function will block if necessary. +pub fn read_any(ctap_hid: &T, buffer: &mut [u8]) -> Result { + let mut reader = Reader::new(ctap_hid, buffer); + scheduling::wait_until(|| !reader.is_empty()); + reader.result() +} + +/// Synchronously reads from a CtapHid into a buffer until it is filled. +/// +/// This function will block if necessary. +pub fn read_all(ctap_hid: &T, buffer: &mut [u8]) -> Result<(), Error> { + let mut reader = Reader::new(ctap_hid, buffer); + scheduling::wait_until(|| reader.is_done()); + reader.result()?; + Ok(()) +} + +/// Synchronously reads exactly one byte. +pub fn read_byte(ctap_hid: &T) -> Result { + let mut byte = 0; + read_any(ctap_hid, core::slice::from_mut(&mut byte))?; + Ok(byte) +} /// Writes from a buffer to the CtapHid. /// @@ -113,260 +110,260 @@ pub fn write(ctap_hid: &T, buffer: &[u8]) -> Result { ctap_hid.write(buffer) } -// /// Writes at least one byte from a buffer to a CtapHid. -// /// -// /// This function will block if necessary. -// pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { -// let mut writer = Writer::new(ctap_hid, buffer); -// scheduling::wait_until(|| !writer.is_empty()); -// writer.result() -// } - -// /// Writes from a buffer to a CtapHid until everything has been written. -// /// -// /// This function will block if necessary. -// pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { -// let mut writer = Writer::new(ctap_hid, buffer); -// scheduling::wait_until(|| writer.is_done()); -// writer.result()?; -// Ok(()) -// } - -// /// Flushes the CtapHid (in case reads or writes are buffered). -// pub fn flush(ctap_hid: &T) -> Result<(), Error> { -// ctap_hid.flush() -// } - -// /// Asynchronously listens for event notifications. -// pub struct Listener<'a, T: CtapHid> { -// ctap_hid: &'a T, -// event: Event, -// notified: &'static Cell, -// } - -// impl<'a, T: CtapHid> Listener<'a, T> { -// /// Starts listening for the provided event until dropped. -// pub fn new(ctap_hid: &'a T, event: Event) -> Self { -// let notified = Box::leak(Box::new(Cell::new(true))); -// let func = Self::call; -// let data = notified as *mut _ as *const u8; -// unsafe { ctap_hid.register(event, func, data) }.unwrap(); -// Listener { ctap_hid, event, notified } -// } - -// /// Returns whether the event triggered since the last call. -// pub fn is_notified(&mut self) -> bool { -// self.notified.replace(false) -// } - -// extern "C" fn call(data: *const u8) { -// let notified = unsafe { &*(data as *const Cell) }; -// notified.set(true); -// } -// } - -// impl<'a, T: CtapHid> Drop for Listener<'a, T> { -// fn drop(&mut self) { -// self.ctap_hid.unregister(self.event).unwrap(); -// drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); -// } -// } - -// /// Asynchronously reads delimited frames. -// /// -// /// If you want to read at most a given amount instead, use [`Reader`]. -// pub struct DelimitedReader<'a, T: CtapHid> { -// listener: Listener<'a, T>, -// buffer: Vec, -// frame: Option, // index of first delimiter in buffer, if any -// delimiter: u8, -// } - -// impl<'a, T: CtapHid> DelimitedReader<'a, T> { -// /// Starts reading delimited frames from a CtapHid. -// pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { -// let listener = Listener::new(ctap_hid, Event::Read); -// DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } -// } - -// /// Returns the next delimited frame (including the delimiter), if any. -// /// -// /// This function should be called until it returns `None` before waiting for callback again. -// /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing -// /// data has not been read. -// pub fn next_frame(&mut self) -> Option> { -// if self.frame.is_none() && self.listener.is_notified() { -// self.flush(); -// } -// self.frame.map(|len| { -// let mut frame = self.buffer.split_off(len + 1); -// core::mem::swap(&mut frame, &mut self.buffer); -// self.frame = self.buffer.iter().position(|&x| x == self.delimiter); -// frame -// }) -// } - -// /// Stops reading and returns the current buffer. -// /// -// /// The buffer may contain multiple delimited frames. -// pub fn stop(self) -> Vec { -// self.buffer -// } - -// fn flush(&mut self) { -// while self.read() {} -// } - -// fn read(&mut self) -> bool { -// let mut data = [0; 32]; -// let len = self.listener.ctap_hid.read(&mut data).unwrap(); -// let pos = self.buffer.len(); -// self.buffer.extend_from_slice(&data[.. len]); -// if self.frame.is_none() { -// for i in pos .. pos + len { -// if self.buffer[i] == self.delimiter { -// self.frame = Some(i); -// break; -// } -// } -// } -// len == data.len() -// } -// } - -// /// Asynchronously reads into the provided buffer. -// /// -// /// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. -// #[must_use] -// pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); - -// impl<'a, T: CtapHid> Reader<'a, T> { -// /// Asynchronously reads from a CtapHid into a buffer. -// pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { -// Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) -// } - -// /// Returns whether anything has been read (or an error occurred). -// pub fn is_empty(&mut self) -> bool { -// self.0.is_empty() -// } - -// /// Returns whether everything has been read (or an error occurred). -// pub fn is_done(&mut self) -> bool { -// self.0.is_done() -// } - -// /// Returns how many bytes were read (or if an error occurred). -// pub fn result(self) -> Result { -// self.0.result() -// } -// } - -// /// Asynchronously writes from the provided buffer. -// #[must_use] -// pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); - -// impl<'a, T: CtapHid> Writer<'a, T> { -// /// Asynchronously writes from a buffer to a CtapHid. -// pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { -// Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) -// } - -// /// Returns whether anything has been written (or an error occurred). -// pub fn is_empty(&mut self) -> bool { -// self.0.is_empty() -// } - -// /// Returns whether everything has been written (or an error occurred). -// pub fn is_done(&mut self) -> bool { -// self.0.is_done() -// } - -// /// Returns how many bytes were written (or if an error occurred). -// pub fn result(self) -> Result { -// self.0.result() -// } -// } - -// struct Updater<'a, T: CtapHid> { -// // The listener is alive as long as not done (see `should_listen()`). -// listener: Option>, -// kind: Kind<'a>, -// result: Result, -// } - -// impl<'a, T: CtapHid> Updater<'a, T> { -// fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { -// let event = kind.event(); -// let mut result = Updater { listener: None, kind, result: Ok(0) }; -// if result.should_listen() { -// result.listener = Some(Listener::new(ctap_hid, event)); -// } -// let _ = result.update(); -// result -// } - -// fn is_empty(&mut self) -> bool { -// matches!(self.update(), Ok(0)) -// } - -// fn is_done(&mut self) -> bool { -// let _ = self.update(); -// self.listener.is_none() -// } - -// fn result(mut self) -> Result { -// self.update() -// } - -// fn update(&mut self) -> Result { -// let listener = match &mut self.listener { -// Some(x) => x, -// None => return self.result, -// }; -// if !listener.is_notified() { -// return self.result; -// } -// let pos = self.result.as_mut().unwrap(); -// match self.kind.update(listener.ctap_hid, *pos) { -// Ok(len) => *pos += len, -// err => self.result = err, -// } -// if !self.should_listen() { -// self.listener = None; -// } -// self.result -// } - -// fn should_listen(&self) -> bool { -// matches!(self.result, Ok(len) if len < self.kind.len()) -// } -// } - -// enum Kind<'a> { -// Reader { buffer: &'a mut [u8] }, -// Writer { buffer: &'a [u8] }, -// } - -// impl<'a> Kind<'a> { -// fn event(&self) -> Event { -// match self { -// Kind::Reader { .. } => Event::Read, -// Kind::Writer { .. } => Event::Write, -// } -// } - -// fn len(&self) -> usize { -// match self { -// Kind::Reader { buffer } => buffer.len(), -// Kind::Writer { buffer } => buffer.len(), -// } -// } - -// fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { -// match self { -// Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), -// Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), -// } -// } -// } +/// Writes at least one byte from a buffer to a CtapHid. +/// +/// This function will block if necessary. +pub fn write_any(ctap_hid: &T, buffer: &[u8]) -> Result { + let mut writer = Writer::new(ctap_hid, buffer); + scheduling::wait_until(|| !writer.is_empty()); + writer.result() +} + +/// Writes from a buffer to a CtapHid until everything has been written. +/// +/// This function will block if necessary. +pub fn write_all(ctap_hid: &T, buffer: &[u8]) -> Result<(), Error> { + let mut writer = Writer::new(ctap_hid, buffer); + scheduling::wait_until(|| writer.is_done()); + writer.result()?; + Ok(()) +} + +/// Flushes the CtapHid (in case reads or writes are buffered). +pub fn flush(ctap_hid: &T) -> Result<(), Error> { + ctap_hid.flush() +} + +/// Asynchronously listens for event notifications. +pub struct Listener<'a, T: CtapHid> { + ctap_hid: &'a T, + event: Event, + notified: &'static Cell, +} + +impl<'a, T: CtapHid> Listener<'a, T> { + /// Starts listening for the provided event until dropped. + pub fn new(ctap_hid: &'a T, event: Event) -> Self { + let notified = Box::leak(Box::new(Cell::new(true))); + let func = Self::call; + let data = notified as *mut _ as *const u8; + unsafe { ctap_hid.register(event, func, data) }.unwrap(); + Listener { ctap_hid, event, notified } + } + + /// Returns whether the event triggered since the last call. + pub fn is_notified(&mut self) -> bool { + self.notified.replace(false) + } + + extern "C" fn call(data: *const u8) { + let notified = unsafe { &*(data as *const Cell) }; + notified.set(true); + } +} + +impl<'a, T: CtapHid> Drop for Listener<'a, T> { + fn drop(&mut self) { + self.ctap_hid.unregister(self.event).unwrap(); + drop(unsafe { Box::from_raw(self.notified.as_ptr()) }); + } +} + +/// Asynchronously reads delimited frames. +/// +/// If you want to read at most a given amount instead, use [`Reader`]. +pub struct DelimitedReader<'a, T: CtapHid> { + listener: Listener<'a, T>, + buffer: Vec, + frame: Option, // index of first delimiter in buffer, if any + delimiter: u8, +} + +impl<'a, T: CtapHid> DelimitedReader<'a, T> { + /// Starts reading delimited frames from a CtapHid. + pub fn new(ctap_hid: &'a T, delimiter: u8) -> Self { + let listener = Listener::new(ctap_hid, Event::Read); + DelimitedReader { listener, buffer: Vec::new(), frame: None, delimiter } + } + + /// Returns the next delimited frame (including the delimiter), if any. + /// + /// This function should be called until it returns `None` before waiting for callback again. + /// Otherwise, it may be possible that the platform doesn't notify for new data if the existing + /// data has not been read. + pub fn next_frame(&mut self) -> Option> { + if self.frame.is_none() && self.listener.is_notified() { + self.flush(); + } + self.frame.map(|len| { + let mut frame = self.buffer.split_off(len + 1); + core::mem::swap(&mut frame, &mut self.buffer); + self.frame = self.buffer.iter().position(|&x| x == self.delimiter); + frame + }) + } + + /// Stops reading and returns the current buffer. + /// + /// The buffer may contain multiple delimited frames. + pub fn stop(self) -> Vec { + self.buffer + } + + fn flush(&mut self) { + while self.read() {} + } + + fn read(&mut self) -> bool { + let mut data = [0; 32]; + let len = self.listener.ctap_hid.read(&mut data).unwrap(); + let pos = self.buffer.len(); + self.buffer.extend_from_slice(&data[.. len]); + if self.frame.is_none() { + for i in pos .. pos + len { + if self.buffer[i] == self.delimiter { + self.frame = Some(i); + break; + } + } + } + len == data.len() + } +} + +/// Asynchronously reads into the provided buffer. +/// +/// If instead you want to continuously read delimited frames, use [`DelimitedReader`]. +#[must_use] +pub struct Reader<'a, T: CtapHid>(Updater<'a, T>); + +impl<'a, T: CtapHid> Reader<'a, T> { + /// Asynchronously reads from a CtapHid into a buffer. + pub fn new(ctap_hid: &'a T, buffer: &'a mut [u8]) -> Self { + Reader(Updater::new(ctap_hid, Kind::Reader { buffer })) + } + + /// Returns whether anything has been read (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been read (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were read (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +/// Asynchronously writes from the provided buffer. +#[must_use] +pub struct Writer<'a, T: CtapHid>(Updater<'a, T>); + +impl<'a, T: CtapHid> Writer<'a, T> { + /// Asynchronously writes from a buffer to a CtapHid. + pub fn new(ctap_hid: &'a T, buffer: &'a [u8]) -> Self { + Writer(Updater::new(ctap_hid, Kind::Writer { buffer })) + } + + /// Returns whether anything has been written (or an error occurred). + pub fn is_empty(&mut self) -> bool { + self.0.is_empty() + } + + /// Returns whether everything has been written (or an error occurred). + pub fn is_done(&mut self) -> bool { + self.0.is_done() + } + + /// Returns how many bytes were written (or if an error occurred). + pub fn result(self) -> Result { + self.0.result() + } +} + +struct Updater<'a, T: CtapHid> { + // The listener is alive as long as not done (see `should_listen()`). + listener: Option>, + kind: Kind<'a>, + result: Result, +} + +impl<'a, T: CtapHid> Updater<'a, T> { + fn new(ctap_hid: &'a T, kind: Kind<'a>) -> Self { + let event = kind.event(); + let mut result = Updater { listener: None, kind, result: Ok(0) }; + if result.should_listen() { + result.listener = Some(Listener::new(ctap_hid, event)); + } + let _ = result.update(); + result + } + + fn is_empty(&mut self) -> bool { + matches!(self.update(), Ok(0)) + } + + fn is_done(&mut self) -> bool { + let _ = self.update(); + self.listener.is_none() + } + + fn result(mut self) -> Result { + self.update() + } + + fn update(&mut self) -> Result { + let listener = match &mut self.listener { + Some(x) => x, + None => return self.result, + }; + if !listener.is_notified() { + return self.result; + } + let pos = self.result.as_mut().unwrap(); + match self.kind.update(listener.ctap_hid, *pos) { + Ok(len) => *pos += len, + err => self.result = err, + } + if !self.should_listen() { + self.listener = None; + } + self.result + } + + fn should_listen(&self) -> bool { + matches!(self.result, Ok(len) if len < self.kind.len()) + } +} + +enum Kind<'a> { + Reader { buffer: &'a mut [u8] }, + Writer { buffer: &'a [u8] }, +} + +impl<'a> Kind<'a> { + fn event(&self) -> Event { + match self { + Kind::Reader { .. } => Event::Read, + Kind::Writer { .. } => Event::Write, + } + } + + fn len(&self) -> usize { + match self { + Kind::Reader { buffer } => buffer.len(), + Kind::Writer { buffer } => buffer.len(), + } + } + + fn update(&mut self, ctap_hid: &impl CtapHid, pos: usize) -> Result { + match self { + Kind::Reader { buffer } => ctap_hid.read(&mut buffer[pos ..]), + Kind::Writer { buffer } => ctap_hid.write(&buffer[pos ..]), + } + } +} diff --git a/crates/prelude/src/usb.rs b/crates/prelude/src/usb.rs index d92e3605..67c8727b 100644 --- a/crates/prelude/src/usb.rs +++ b/crates/prelude/src/usb.rs @@ -20,5 +20,5 @@ #[cfg(feature = "api-usb-serial")] pub mod serial; -// TODO: Add feature +#[cfg(feature = "api-usb-ctap")] pub mod ctap;