Tock V2 port - rebased and updated (#620)
* Changes from #580 * fixes USB cancel panic * style fixes * Update src/env/tock/storage.rs Co-authored-by: Zach Halvorsen <zhalvorsen@google.com> --------- Co-authored-by: Zach Halvorsen <zhalvorsen@google.com>
This commit is contained in:
121
patches/libtock-rs/01-alloc-init-feature.patch
Normal file
121
patches/libtock-rs/01-alloc-init-feature.patch
Normal file
@@ -0,0 +1,121 @@
|
||||
diff --git a/apis/buttons/src/lib.rs b/apis/buttons/src/lib.rs
|
||||
index 565970f..f5d08b5 100644
|
||||
--- a/apis/buttons/src/lib.rs
|
||||
+++ b/apis/buttons/src/lib.rs
|
||||
@@ -135,7 +135,7 @@ mod tests;
|
||||
// Driver number and command IDs
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
-const DRIVER_NUM: u32 = 3;
|
||||
+pub const DRIVER_NUM: u32 = 3;
|
||||
|
||||
// Command IDs
|
||||
const BUTTONS_COUNT: u32 = 0;
|
||||
diff --git a/runtime/Cargo.toml b/runtime/Cargo.toml
|
||||
index a863392..3509a40 100644
|
||||
--- a/runtime/Cargo.toml
|
||||
+++ b/runtime/Cargo.toml
|
||||
@@ -14,6 +14,9 @@ libtock_platform = { path = "../platform" }
|
||||
|
||||
[features]
|
||||
|
||||
+# Initialize an allocator for access to heap memory
|
||||
+alloc_init = []
|
||||
+
|
||||
# By default, libtock_runtime looks for the LIBTOCK_PLATFORM variable to decide
|
||||
# what layout file to use. If you are providing your own linker script, set
|
||||
# no_auto_layout to disable the layout file logic.
|
||||
diff --git a/runtime/src/lib.rs b/runtime/src/lib.rs
|
||||
index 1054e41..b5c519a 100644
|
||||
--- a/runtime/src/lib.rs
|
||||
+++ b/runtime/src/lib.rs
|
||||
@@ -19,6 +19,7 @@
|
||||
//! and provide its own layout file.
|
||||
|
||||
#![no_std]
|
||||
+#![feature(strict_provenance)]
|
||||
#![warn(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
pub mod startup;
|
||||
diff --git a/runtime/src/startup/mod.rs b/runtime/src/startup/mod.rs
|
||||
index 769bfe9..052479f 100644
|
||||
--- a/runtime/src/startup/mod.rs
|
||||
+++ b/runtime/src/startup/mod.rs
|
||||
@@ -10,6 +10,8 @@ core::arch::global_asm!(include_str!("asm_arm.s"));
|
||||
#[cfg(target_arch = "riscv32")]
|
||||
core::arch::global_asm!(include_str!("asm_riscv32.s"));
|
||||
|
||||
+static APP_HEAP_SIZE: Option<&'static str> = option_env!("APP_HEAP_SIZE");
|
||||
+
|
||||
/// `set_main!` is used to tell `libtock_runtime` where the process binary's
|
||||
/// `main` function is. The process binary's `main` function must have the
|
||||
/// signature `FnOnce() -> T`, where T is some concrete type that implements
|
||||
@@ -83,6 +85,8 @@ extern "C" fn rust_start() -> ! {
|
||||
extern "Rust" {
|
||||
fn libtock_unsafe_main() -> !;
|
||||
static rt_header: RtHeader;
|
||||
+ #[cfg(feature = "alloc_init")]
|
||||
+ fn libtock_alloc_init(heap_bottom: *mut u8, heap_size: usize);
|
||||
}
|
||||
|
||||
// TODO: Implement a safe memop API in libtock_platform and migrate these
|
||||
@@ -92,16 +96,46 @@ extern "C" fn rust_start() -> ! {
|
||||
// impact the execution of this process.
|
||||
#[cfg(not(feature = "no_debug_memop"))]
|
||||
unsafe {
|
||||
+ // specify the top of the application stack which grows downwards
|
||||
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
|
||||
10u32.into(),
|
||||
rt_header.stack_top.into(),
|
||||
]);
|
||||
+
|
||||
+ // specify the start of the application heap which grows upwards
|
||||
TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([
|
||||
11u32.into(),
|
||||
rt_header.initial_break.into(),
|
||||
]);
|
||||
}
|
||||
|
||||
+ #[cfg(feature = "alloc_init")]
|
||||
+ {
|
||||
+ let app_heap_size: usize = match APP_HEAP_SIZE {
|
||||
+ Some(var) => var
|
||||
+ .parse()
|
||||
+ .ok()
|
||||
+ .expect("could not parse APP_HEAP_SIZE as usize!"),
|
||||
+ None => 9000,
|
||||
+ };
|
||||
+
|
||||
+ // the heap starts after the `bss` section
|
||||
+ let app_heap_bottom = unsafe { rt_header.bss_start.add(rt_header.bss_size) };
|
||||
+
|
||||
+ assert_eq!(app_heap_bottom.addr(), unsafe {
|
||||
+ rt_header.bss_start.addr() + rt_header.bss_size
|
||||
+ });
|
||||
+
|
||||
+ let app_heap_end = unsafe { app_heap_bottom.add(app_heap_size) };
|
||||
+
|
||||
+ unsafe {
|
||||
+ // tell the kernel the new app heap break (which is the upper address bound of the process)
|
||||
+ TockSyscalls::syscall2::<{ syscall_class::MEMOP }>([0u32.into(), app_heap_end.into()]);
|
||||
+
|
||||
+ libtock_alloc_init(app_heap_bottom, app_heap_size);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
// Safety: libtock_unsafe_main is defined by the set_main! macro, and its
|
||||
// signature matches the signature in the `extern` block in this function.
|
||||
unsafe {
|
||||
diff --git a/rust-toolchain b/rust-toolchain
|
||||
index e911a96..d139e12 100644
|
||||
--- a/rust-toolchain
|
||||
+++ b/rust-toolchain
|
||||
@@ -1,7 +1,7 @@
|
||||
[toolchain]
|
||||
# See https://rust-lang.github.io/rustup-components-history/ for a list of
|
||||
# recently nightlies and what components are available for them.
|
||||
-channel = "nightly-2022-06-10"
|
||||
+channel = "nightly-2023-02-01"
|
||||
components = ["clippy", "miri", "rustfmt"]
|
||||
targets = ["thumbv7em-none-eabi",
|
||||
"riscv32imac-unknown-none-elf",
|
||||
@@ -1,12 +0,0 @@
|
||||
diff --git a/core/src/stack_size.rs b/core/src/stack_size.rs
|
||||
index 9145393..ef55383 100644
|
||||
--- a/core/src/stack_size.rs
|
||||
+++ b/core/src/stack_size.rs
|
||||
@@ -14,6 +14,7 @@ macro_rules! stack_size {
|
||||
{$size:expr} => {
|
||||
#[no_mangle]
|
||||
#[link_section = ".stack_buffer"]
|
||||
+ #[cfg(not(target_os = "macos"))]
|
||||
pub static mut STACK_MEMORY: [u8; $size] = [0; $size];
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
diff --git a/Cargo.toml b/Cargo.toml
|
||||
index 06acc26d2..bd1bbd58f 100644
|
||||
index 04cb39334..596cacda4 100644
|
||||
--- a/Cargo.toml
|
||||
+++ b/Cargo.toml
|
||||
@@ -20,7 +20,13 @@ members = [
|
||||
@@ -23,7 +23,13 @@ members = [
|
||||
"boards/msp_exp432p401r",
|
||||
"boards/microbit_v2",
|
||||
"boards/nordic/nrf52840dk",
|
||||
@@ -1,9 +1,9 @@
|
||||
diff --git a/boards/components/src/ctap.rs b/boards/components/src/ctap.rs
|
||||
deleted file mode 100644
|
||||
index 5e30702b0..000000000
|
||||
index 87b4ded9f..000000000
|
||||
--- a/boards/components/src/ctap.rs
|
||||
+++ /dev/null
|
||||
@@ -1,127 +0,0 @@
|
||||
@@ -1,130 +0,0 @@
|
||||
-//! Component for CTAP HID over USB support.
|
||||
-//!
|
||||
-//! This provides a component for using the CTAP driver. This allows for
|
||||
@@ -57,31 +57,34 @@ index 5e30702b0..000000000
|
||||
-}
|
||||
-
|
||||
-pub struct CtapComponent<U: 'static + hil::usb::UsbController<'static>> {
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- driver_num: usize,
|
||||
- usb: &'static U,
|
||||
- vendor_id: u16,
|
||||
- product_id: u16,
|
||||
- strings: &'static [&'static str; 3],
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- send_buffer: &'static mut [u8; 64],
|
||||
- recv_buffer: &'static mut [u8; 64],
|
||||
-}
|
||||
-
|
||||
-impl<U: 'static + hil::usb::UsbController<'static>> CtapComponent<U> {
|
||||
- pub fn new(
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- driver_num: usize,
|
||||
- usb: &'static U,
|
||||
- vendor_id: u16,
|
||||
- product_id: u16,
|
||||
- strings: &'static [&'static str; 3],
|
||||
- board_kernel: &'static kernel::Kernel,
|
||||
- send_buffer: &'static mut [u8; 64],
|
||||
- recv_buffer: &'static mut [u8; 64],
|
||||
- ) -> CtapComponent<U> {
|
||||
- CtapComponent {
|
||||
- board_kernel,
|
||||
- driver_num,
|
||||
- usb,
|
||||
- vendor_id,
|
||||
- product_id,
|
||||
- strings,
|
||||
- board_kernel,
|
||||
- send_buffer,
|
||||
- recv_buffer,
|
||||
- }
|
||||
@@ -122,7 +125,7 @@ index 5e30702b0..000000000
|
||||
- Some(ctap),
|
||||
- self.send_buffer,
|
||||
- self.recv_buffer,
|
||||
- self.board_kernel.create_grant(&grant_cap),
|
||||
- self.board_kernel.create_grant(self.driver_num, &grant_cap),
|
||||
- )
|
||||
- );
|
||||
-
|
||||
@@ -132,23 +135,23 @@ index 5e30702b0..000000000
|
||||
- }
|
||||
-}
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index 95625f91f..cd542609e 100644
|
||||
index 120403652..92d45f7e3 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -10,7 +10,6 @@ pub mod button;
|
||||
@@ -14,7 +14,6 @@ pub mod ccs811;
|
||||
pub mod cdc;
|
||||
pub mod console;
|
||||
pub mod crc;
|
||||
-pub mod ctap;
|
||||
pub mod debug_queue;
|
||||
pub mod debug_writer;
|
||||
pub mod firmware_protection;
|
||||
pub mod digest;
|
||||
diff --git a/capsules/src/usb/ctap.rs b/capsules/src/usb/ctap.rs
|
||||
deleted file mode 100644
|
||||
index 32cb2d6f1..000000000
|
||||
index d085b7286..000000000
|
||||
--- a/capsules/src/usb/ctap.rs
|
||||
+++ /dev/null
|
||||
@@ -1,410 +0,0 @@
|
||||
@@ -1,406 +0,0 @@
|
||||
-//! Client to Authenticator Protocol CTAPv2 over USB HID
|
||||
-//!
|
||||
-//! Based on the spec avaliable at: <https://fidoalliance.org/specs/fido-v2.0-id-20180227/fido-client-to-authenticator-protocol-v2.0-id-20180227.html>
|
||||
@@ -169,11 +172,11 @@ index 32cb2d6f1..000000000
|
||||
-use super::descriptors::TransferDirection;
|
||||
-use super::usbc_client_ctrl::ClientCtrl;
|
||||
-
|
||||
-use kernel::common::cells::OptionalCell;
|
||||
-use kernel::common::cells::TakeCell;
|
||||
-use kernel::hil;
|
||||
-use kernel::hil::usb::TransferType;
|
||||
-use kernel::ReturnCode;
|
||||
-use kernel::utilities::cells::OptionalCell;
|
||||
-use kernel::utilities::cells::TakeCell;
|
||||
-use kernel::ErrorCode;
|
||||
-
|
||||
-/// Use 1 Interrupt transfer IN/OUT endpoint
|
||||
-const ENDPOINT_NUM: usize = 1;
|
||||
@@ -349,7 +352,7 @@ index 32cb2d6f1..000000000
|
||||
- fn send_buffer(
|
||||
- &'a self,
|
||||
- send: &'static mut [u8; 64],
|
||||
- ) -> Result<usize, (ReturnCode, &'static mut [u8; 64])> {
|
||||
- ) -> Result<usize, (ErrorCode, &'static mut [u8; 64])> {
|
||||
- let len = send.len();
|
||||
-
|
||||
- self.send_buffer.replace(send);
|
||||
@@ -358,17 +361,17 @@ index 32cb2d6f1..000000000
|
||||
- Ok(len)
|
||||
- }
|
||||
-
|
||||
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
|
||||
- fn send_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
|
||||
- match self.send_buffer.take() {
|
||||
- Some(buf) => Ok(buf),
|
||||
- None => Err(ReturnCode::EBUSY),
|
||||
- None => Err(ErrorCode::BUSY),
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn receive_buffer(
|
||||
- &'a self,
|
||||
- recv: &'static mut [u8; 64],
|
||||
- ) -> Result<(), (ReturnCode, &'static mut [u8; 64])> {
|
||||
- ) -> Result<(), (ErrorCode, &'static mut [u8; 64])> {
|
||||
- self.recv_buffer.replace(recv);
|
||||
-
|
||||
- if self.saved_endpoint.is_some() {
|
||||
@@ -376,11 +379,7 @@ index 32cb2d6f1..000000000
|
||||
- if self.can_receive() {
|
||||
- self.recv_buffer.take().map(|buf| {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_received(
|
||||
- ReturnCode::SUCCESS,
|
||||
- buf,
|
||||
- self.saved_endpoint.take().unwrap(),
|
||||
- );
|
||||
- client.packet_received(Ok(()), buf, self.saved_endpoint.take().unwrap());
|
||||
- });
|
||||
- });
|
||||
- // Reset the offset
|
||||
@@ -394,11 +393,11 @@ index 32cb2d6f1..000000000
|
||||
- Ok(())
|
||||
- }
|
||||
-
|
||||
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ReturnCode> {
|
||||
- fn receive_cancel(&'a self) -> Result<&'static mut [u8; 64], ErrorCode> {
|
||||
- self.saved_endpoint.take();
|
||||
- match self.recv_buffer.take() {
|
||||
- Some(buf) => Ok(buf),
|
||||
- None => Err(ReturnCode::EBUSY),
|
||||
- None => Err(ErrorCode::BUSY),
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
@@ -524,7 +523,7 @@ index 32cb2d6f1..000000000
|
||||
- if total_received_bytes >= self.recv_len.get() {
|
||||
- if self.can_receive() {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_received(ReturnCode::SUCCESS, buf, endpoint);
|
||||
- client.packet_received(Ok(()), buf, endpoint);
|
||||
- });
|
||||
- // Reset the offset
|
||||
- self.recv_offset.set(0);
|
||||
@@ -554,18 +553,18 @@ index 32cb2d6f1..000000000
|
||||
- fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
- self.send_buffer.take().map(|buf| {
|
||||
- self.client.map(move |client| {
|
||||
- client.packet_transmitted(ReturnCode::SUCCESS, buf, endpoint);
|
||||
- client.packet_transmitted(Ok(()), buf, endpoint);
|
||||
- });
|
||||
- });
|
||||
- }
|
||||
-}
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 17cab4c23..3f3a4f646 100644
|
||||
index 6d5daa444..767f5de83 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,5 +1,4 @@
|
||||
pub mod cdc;
|
||||
-pub mod ctap;
|
||||
pub mod descriptors;
|
||||
pub mod usb_ctap;
|
||||
pub mod usb_user;
|
||||
pub mod usbc_client;
|
||||
@@ -1,864 +0,0 @@
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index 64fe46b7b..a3bbe724b 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -52,3 +52,4 @@ pub mod tickv;
|
||||
pub mod touch;
|
||||
pub mod udp_driver;
|
||||
pub mod udp_mux;
|
||||
+pub mod usb_ctap;
|
||||
diff --git a/boards/components/src/usb_ctap.rs b/boards/components/src/usb_ctap.rs
|
||||
new file mode 100644
|
||||
index 000000000..69e95c3c7
|
||||
--- /dev/null
|
||||
+++ b/boards/components/src/usb_ctap.rs
|
||||
@@ -0,0 +1,88 @@
|
||||
+//! Component for CTAP over USB.
|
||||
+
|
||||
+use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
|
||||
+use capsules::usb::usbc_ctap_hid::ClientCtapHID;
|
||||
+use core::mem::MaybeUninit;
|
||||
+use kernel::capabilities;
|
||||
+use kernel::component::Component;
|
||||
+use kernel::create_capability;
|
||||
+use kernel::hil;
|
||||
+use kernel::static_init_half;
|
||||
+
|
||||
+// Setup static space for the objects.
|
||||
+#[macro_export]
|
||||
+macro_rules! usb_ctap_component_buf {
|
||||
+ ($C:ty) => {{
|
||||
+ use capsules::usb::usb_ctap::CtapUsbSyscallDriver;
|
||||
+ use capsules::usb::usbc_ctap_hid::ClientCtapHID;
|
||||
+ use core::mem::MaybeUninit;
|
||||
+ static mut BUF1: MaybeUninit<ClientCtapHID<'static, 'static, $C>> = MaybeUninit::uninit();
|
||||
+ static mut BUF2: MaybeUninit<CtapUsbSyscallDriver<'static, 'static, $C>> =
|
||||
+ MaybeUninit::uninit();
|
||||
+ (&mut BUF1, &mut BUF2)
|
||||
+ };};
|
||||
+}
|
||||
+
|
||||
+pub struct UsbCtapComponent<C: 'static + hil::usb::UsbController<'static>> {
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ controller: &'static C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::usb::UsbController<'static>> UsbCtapComponent<C> {
|
||||
+ pub fn new(
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ controller: &'static C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+ ) -> Self {
|
||||
+ Self {
|
||||
+ board_kernel,
|
||||
+ controller,
|
||||
+ max_ctrl_packet_size,
|
||||
+ vendor_id,
|
||||
+ product_id,
|
||||
+ strings,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::usb::UsbController<'static>> Component for UsbCtapComponent<C> {
|
||||
+ type StaticInput = (
|
||||
+ &'static mut MaybeUninit<ClientCtapHID<'static, 'static, C>>,
|
||||
+ &'static mut MaybeUninit<CtapUsbSyscallDriver<'static, 'static, C>>,
|
||||
+ );
|
||||
+ type Output = &'static CtapUsbSyscallDriver<'static, 'static, C>;
|
||||
+
|
||||
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
|
||||
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
+
|
||||
+ let usb_ctap = static_init_half!(
|
||||
+ static_buffer.0,
|
||||
+ ClientCtapHID<'static, 'static, C>,
|
||||
+ ClientCtapHID::new(
|
||||
+ self.controller,
|
||||
+ self.max_ctrl_packet_size,
|
||||
+ self.vendor_id,
|
||||
+ self.product_id,
|
||||
+ self.strings,
|
||||
+ )
|
||||
+ );
|
||||
+ self.controller.set_client(usb_ctap);
|
||||
+
|
||||
+ // Configure the USB userspace driver
|
||||
+ let usb_driver = static_init_half!(
|
||||
+ static_buffer.1,
|
||||
+ CtapUsbSyscallDriver<'static, 'static, C>,
|
||||
+ CtapUsbSyscallDriver::new(usb_ctap, self.board_kernel.create_grant(&grant_cap))
|
||||
+ );
|
||||
+ usb_ctap.set_client(usb_driver);
|
||||
+
|
||||
+ usb_driver
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
|
||||
index 3dad0f50b..b6124c4c0 100644
|
||||
--- a/capsules/src/driver.rs
|
||||
+++ b/capsules/src/driver.rs
|
||||
@@ -26,6 +26,7 @@ pub enum NUM {
|
||||
I2cMaster = 0x20003,
|
||||
UsbUser = 0x20005,
|
||||
I2cMasterSlave = 0x20006,
|
||||
+ UsbCtap = 0x20009,
|
||||
|
||||
// Radio
|
||||
BleAdvertising = 0x30000,
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 6d5daa444..17cab4c23 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,6 +1,8 @@
|
||||
pub mod cdc;
|
||||
pub mod ctap;
|
||||
pub mod descriptors;
|
||||
+pub mod usb_ctap;
|
||||
pub mod usb_user;
|
||||
pub mod usbc_client;
|
||||
pub mod usbc_client_ctrl;
|
||||
+pub mod usbc_ctap_hid;
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
new file mode 100644
|
||||
index 000000000..da3d16d85
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -0,0 +1,355 @@
|
||||
+use super::usbc_ctap_hid::ClientCtapHID;
|
||||
+use kernel::hil;
|
||||
+use kernel::hil::usb::Client;
|
||||
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
+
|
||||
+/// Syscall number
|
||||
+use crate::driver;
|
||||
+pub const DRIVER_NUM: usize = driver::NUM::UsbCtap as usize;
|
||||
+
|
||||
+pub const CTAP_CMD_CHECK: usize = 0;
|
||||
+pub const CTAP_CMD_CONNECT: usize = 1;
|
||||
+pub const CTAP_CMD_TRANSMIT: usize = 2;
|
||||
+pub const CTAP_CMD_RECEIVE: usize = 3;
|
||||
+pub const CTAP_CMD_TRANSMIT_OR_RECEIVE: usize = 4;
|
||||
+pub const CTAP_CMD_CANCEL: usize = 5;
|
||||
+
|
||||
+pub const CTAP_ALLOW_TRANSMIT: usize = 1;
|
||||
+pub const CTAP_ALLOW_RECEIVE: usize = 2;
|
||||
+pub const CTAP_ALLOW_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
+
|
||||
+pub const CTAP_SUBSCRIBE_TRANSMIT: usize = 1;
|
||||
+pub const CTAP_SUBSCRIBE_RECEIVE: usize = 2;
|
||||
+pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
+
|
||||
+pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
|
||||
+pub const CTAP_CALLBACK_RECEIVED: usize = 2;
|
||||
+
|
||||
+#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
+enum Side {
|
||||
+ Transmit,
|
||||
+ Receive,
|
||||
+ TransmitOrReceive,
|
||||
+}
|
||||
+
|
||||
+impl Side {
|
||||
+ fn can_transmit(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Transmit | Side::TransmitOrReceive => true,
|
||||
+ Side::Receive => false,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn can_receive(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Receive | Side::TransmitOrReceive => true,
|
||||
+ Side::Transmit => false,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
+ // This field tracks this status.
|
||||
+ connected: bool,
|
||||
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
+ // the side to None.
|
||||
+ side: Option<Side>,
|
||||
+ callback: Option<Callback>,
|
||||
+ buffer: Option<AppSlice<Shared, u8>>,
|
||||
+ // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
+ waiting: bool,
|
||||
+}
|
||||
+
|
||||
+impl App {
|
||||
+ fn check_side(&mut self) {
|
||||
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
+ self.side = None;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn set_side(&mut self, side: Side) -> bool {
|
||||
+ match self.side {
|
||||
+ None => {
|
||||
+ self.side = Some(side);
|
||||
+ true
|
||||
+ }
|
||||
+ Some(app_side) => side == app_side,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub trait CtapUsbClient {
|
||||
+ // Whether this client is ready to receive a packet. This must be checked before calling
|
||||
+ // packet_received().
|
||||
+ fn can_receive_packet(&self) -> bool;
|
||||
+
|
||||
+ // Signal to the client that a packet has been received.
|
||||
+ fn packet_received(&self, packet: &[u8; 64]);
|
||||
+
|
||||
+ // Signal to the client that a packet has been transmitted.
|
||||
+ fn packet_transmitted(&self);
|
||||
+}
|
||||
+
|
||||
+pub struct CtapUsbSyscallDriver<'a, 'b, C: 'a> {
|
||||
+ usb_client: &'a ClientCtapHID<'a, 'b, C>,
|
||||
+ apps: Grant<App>,
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
|
||||
+ CtapUsbSyscallDriver { usb_client, apps }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ fn can_receive_packet(&self) -> bool {
|
||||
+ let mut result = false;
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected {
|
||||
+ result = app.waiting
|
||||
+ && app.side.map_or(false, |side| side.can_receive())
|
||||
+ && app.buffer.is_some();
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ result
|
||||
+ }
|
||||
+
|
||||
+ fn packet_received(&self, packet: &[u8; 64]) {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
|
||||
+ {
|
||||
+ if let Some(buf) = &mut app.buffer {
|
||||
+ // Copy the packet to the app's allowed buffer.
|
||||
+ buf.as_mut().copy_from_slice(packet);
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that a packet is ready.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ }
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn packet_transmitted(&self) {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ if app.connected
|
||||
+ && app.waiting
|
||||
+ && app.side.map_or(false, |side| side.can_transmit())
|
||||
+ {
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that the packet was sent.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_TRANSMITED, 0, 0));
|
||||
+ }
|
||||
+ });
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
+ fn allow(
|
||||
+ &self,
|
||||
+ appid: AppId,
|
||||
+ allow_num: usize,
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+ ) -> ReturnCode {
|
||||
+ let side = match allow_num {
|
||||
+ CTAP_ALLOW_TRANSMIT => Side::Transmit,
|
||||
+ CTAP_ALLOW_RECEIVE => Side::Receive,
|
||||
+ CTAP_ALLOW_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
|
||||
+ _ => return ReturnCode::ENOSUPPORT,
|
||||
+ };
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if let Some(buf) = &slice {
|
||||
+ if buf.len() != 64 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ }
|
||||
+ if !app.set_side(side) {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+ app.buffer = slice;
|
||||
+ app.check_side();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+
|
||||
+ fn subscribe(
|
||||
+ &self,
|
||||
+ subscribe_num: usize,
|
||||
+ callback: Option<Callback>,
|
||||
+ appid: AppId,
|
||||
+ ) -> ReturnCode {
|
||||
+ let side = match subscribe_num {
|
||||
+ CTAP_SUBSCRIBE_TRANSMIT => Side::Transmit,
|
||||
+ CTAP_SUBSCRIBE_RECEIVE => Side::Receive,
|
||||
+ CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE => Side::TransmitOrReceive,
|
||||
+ _ => return ReturnCode::ENOSUPPORT,
|
||||
+ };
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if !app.set_side(side) {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+ app.callback = callback;
|
||||
+ app.check_side();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+
|
||||
+ fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
+ match cmd_num {
|
||||
+ CTAP_CMD_CHECK => ReturnCode::SUCCESS,
|
||||
+ CTAP_CMD_CONNECT => {
|
||||
+ // First, check if any app is already connected to this driver.
|
||||
+ let mut busy = false;
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|app, _| {
|
||||
+ busy |= app.connected;
|
||||
+ });
|
||||
+ }
|
||||
+
|
||||
+ self.apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if app.connected {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else if busy {
|
||||
+ ReturnCode::EBUSY
|
||||
+ } else {
|
||||
+ self.usb_client.enable();
|
||||
+ self.usb_client.attach();
|
||||
+ app.connected = true;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into())
|
||||
+ }
|
||||
+ CTAP_CMD_TRANSMIT => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::Transmit) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else if self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
+ {
|
||||
+ app.waiting = true;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_RECEIVE => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::Receive) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else {
|
||||
+ app.waiting = true;
|
||||
+ self.usb_client.receive_packet();
|
||||
+ ReturnCode::SUCCESS
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_TRANSMIT_OR_RECEIVE => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.is_ready_for_command(Side::TransmitOrReceive) {
|
||||
+ if app.waiting {
|
||||
+ ReturnCode::EALREADY
|
||||
+ } else {
|
||||
+ // Indicates to the driver that we can receive any pending packet.
|
||||
+ app.waiting = true;
|
||||
+ self.usb_client.receive_packet();
|
||||
+
|
||||
+ if !app.waiting {
|
||||
+ // The call to receive_packet() collected a pending packet.
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Indicates to the driver that we have a packet to send.
|
||||
+ if self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
+ {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EINVAL
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ CTAP_CMD_CANCEL => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ if !app.connected {
|
||||
+ ReturnCode::ERESERVE
|
||||
+ } else {
|
||||
+ if app.waiting {
|
||||
+ // FIXME: if cancellation failed, the app should still wait. But that
|
||||
+ // doesn't work yet.
|
||||
+ app.waiting = false;
|
||||
+ if self.usb_client.cancel_transaction() {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Cannot cancel now because the transaction is already in process.
|
||||
+ // The app should wait for the callback instead.
|
||||
+ ReturnCode::EBUSY
|
||||
+ }
|
||||
+ } else {
|
||||
+ ReturnCode::EALREADY
|
||||
+ }
|
||||
+ }
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
new file mode 100644
|
||||
index 000000000..642039120
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -0,0 +1,369 @@
|
||||
+//! A USB HID client of the USB hardware interface
|
||||
+
|
||||
+use super::descriptors;
|
||||
+use super::descriptors::Buffer64;
|
||||
+use super::descriptors::DescriptorType;
|
||||
+use super::descriptors::EndpointAddress;
|
||||
+use super::descriptors::EndpointDescriptor;
|
||||
+use super::descriptors::HIDCountryCode;
|
||||
+use super::descriptors::HIDDescriptor;
|
||||
+use super::descriptors::HIDSubordinateDescriptor;
|
||||
+use super::descriptors::InterfaceDescriptor;
|
||||
+use super::descriptors::ReportDescriptor;
|
||||
+use super::descriptors::TransferDirection;
|
||||
+use super::usb_ctap::CtapUsbClient;
|
||||
+use super::usbc_client_ctrl::ClientCtrl;
|
||||
+use core::cell::Cell;
|
||||
+use kernel::common::cells::OptionalCell;
|
||||
+use kernel::debug;
|
||||
+use kernel::hil;
|
||||
+use kernel::hil::usb::TransferType;
|
||||
+
|
||||
+static LANGUAGES: &'static [u16; 1] = &[
|
||||
+ 0x0409, // English (United States)
|
||||
+];
|
||||
+
|
||||
+const ENDPOINT_NUM: usize = 1;
|
||||
+
|
||||
+static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
+ 0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
|
||||
+ 0x09, 0x01, // HID_Usage ( FIDO_USAGE_CTAPHID ),
|
||||
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
|
||||
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
|
||||
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
|
||||
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0xC0, // HID_EndCollection
|
||||
+];
|
||||
+
|
||||
+static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
+ desc: CTAP_REPORT_DESCRIPTOR,
|
||||
+};
|
||||
+
|
||||
+static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
+ typ: DescriptorType::Report,
|
||||
+ len: CTAP_REPORT_DESCRIPTOR.len() as u16,
|
||||
+}];
|
||||
+
|
||||
+static HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
+ hid_class: 0x0110,
|
||||
+ country_code: HIDCountryCode::NotSupported,
|
||||
+ sub_descriptors: HID_SUB_DESCRIPTORS,
|
||||
+};
|
||||
+
|
||||
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
+ client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
+
|
||||
+ // 64-byte buffers for the endpoint
|
||||
+ in_buffer: Buffer64,
|
||||
+ out_buffer: Buffer64,
|
||||
+
|
||||
+ // Interaction with the client
|
||||
+ client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
+ tx_packet: OptionalCell<[u8; 64]>,
|
||||
+ pending_in: Cell<bool>,
|
||||
+ pending_out: Cell<bool>,
|
||||
+ delayed_out: Cell<bool>,
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
+ pub fn new(
|
||||
+ controller: &'a C,
|
||||
+ max_ctrl_packet_size: u8,
|
||||
+ vendor_id: u16,
|
||||
+ product_id: u16,
|
||||
+ strings: &'static [&'static str],
|
||||
+ ) -> Self {
|
||||
+ let interfaces: &mut [InterfaceDescriptor] = &mut [
|
||||
+ // Interface declared in the FIDO2 specification, section 8.1.8.1
|
||||
+ InterfaceDescriptor {
|
||||
+ interface_class: 0x03, // HID
|
||||
+ interface_subclass: 0x00,
|
||||
+ interface_protocol: 0x00,
|
||||
+ ..InterfaceDescriptor::default()
|
||||
+ },
|
||||
+ ];
|
||||
+
|
||||
+ let endpoints: &[&[EndpointDescriptor]] = &[&[
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM,
|
||||
+ TransferDirection::HostToDevice,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM,
|
||||
+ TransferDirection::DeviceToHost,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ ]];
|
||||
+
|
||||
+ let (device_descriptor_buffer, other_descriptor_buffer) =
|
||||
+ descriptors::create_descriptor_buffers(
|
||||
+ descriptors::DeviceDescriptor {
|
||||
+ vendor_id,
|
||||
+ product_id,
|
||||
+ manufacturer_string: 1,
|
||||
+ product_string: 2,
|
||||
+ serial_number_string: 3,
|
||||
+ max_packet_size_ep0: max_ctrl_packet_size,
|
||||
+ ..descriptors::DeviceDescriptor::default()
|
||||
+ },
|
||||
+ descriptors::ConfigurationDescriptor {
|
||||
+ configuration_value: 1,
|
||||
+ ..descriptors::ConfigurationDescriptor::default()
|
||||
+ },
|
||||
+ interfaces,
|
||||
+ endpoints,
|
||||
+ Some(&HID),
|
||||
+ None, // No CDC descriptor array
|
||||
+ );
|
||||
+
|
||||
+ ClientCtapHID {
|
||||
+ client_ctrl: ClientCtrl::new(
|
||||
+ controller,
|
||||
+ device_descriptor_buffer,
|
||||
+ other_descriptor_buffer,
|
||||
+ Some(&HID),
|
||||
+ Some(&CTAP_REPORT),
|
||||
+ LANGUAGES,
|
||||
+ strings,
|
||||
+ ),
|
||||
+ in_buffer: Buffer64::default(),
|
||||
+ out_buffer: Buffer64::default(),
|
||||
+ client: OptionalCell::empty(),
|
||||
+ tx_packet: OptionalCell::empty(),
|
||||
+ pending_in: Cell::new(false),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ delayed_out: Cell::new(false),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
|
||||
+ self.client.set(client);
|
||||
+ }
|
||||
+
|
||||
+ pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
|
||||
+ if self.pending_in.get() {
|
||||
+ // The previous packet has not yet been transmitted, reject the new one.
|
||||
+ false
|
||||
+ } else {
|
||||
+ self.pending_in.set(true);
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ buf.copy_from_slice(packet);
|
||||
+ self.tx_packet.set(buf);
|
||||
+ // Alert the controller that we now have data to send on the Interrupt IN endpoint.
|
||||
+ self.controller().endpoint_resume_in(1);
|
||||
+ true
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn receive_packet(&'a self) -> bool {
|
||||
+ if self.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ false
|
||||
+ } else {
|
||||
+ self.pending_out.set(true);
|
||||
+ // In case we reported Delay before, send the pending packet back to the client.
|
||||
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
+ // packet arrives.
|
||||
+ if self.delayed_out.take() {
|
||||
+ if self.send_packet_to_client() {
|
||||
+ // If that succeeds, alert the controller that we can now
|
||||
+ // receive data on the Interrupt OUT endpoint.
|
||||
+ self.controller().endpoint_resume_out(1);
|
||||
+ }
|
||||
+ }
|
||||
+ true
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Send an OUT packet available in the controller back to the client.
|
||||
+ // This returns false if the client is not ready to receive a packet, and true if the client
|
||||
+ // successfully accepted the packet.
|
||||
+ fn send_packet_to_client(&'a self) -> bool {
|
||||
+ // Copy the packet into a buffer to send to the client.
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
+ buf[i] = x.get();
|
||||
+ }
|
||||
+
|
||||
+ assert!(!self.delayed_out.get());
|
||||
+
|
||||
+ // Notify the client
|
||||
+ if self
|
||||
+ .client
|
||||
+ .map_or(false, |client| client.can_receive_packet())
|
||||
+ {
|
||||
+ assert!(self.pending_out.take());
|
||||
+
|
||||
+ // Clear any pending packet on the transmitting side.
|
||||
+ // It's up to the client to handle the received packet and decide if this packet
|
||||
+ // should be re-transmitted or not.
|
||||
+ self.cancel_in_transaction();
|
||||
+
|
||||
+ self.client.map(|client| client.packet_received(&buf));
|
||||
+ true
|
||||
+ } else {
|
||||
+ // Cannot receive now, indicate a delay to the controller.
|
||||
+ self.delayed_out.set(true);
|
||||
+ false
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn cancel_transaction(&'a self) -> bool {
|
||||
+ self.cancel_in_transaction() | self.cancel_out_transaction()
|
||||
+ }
|
||||
+
|
||||
+ fn cancel_in_transaction(&'a self) -> bool {
|
||||
+ self.tx_packet.take();
|
||||
+ self.pending_in.take()
|
||||
+ }
|
||||
+
|
||||
+ fn cancel_out_transaction(&'a self) -> bool {
|
||||
+ self.pending_out.take()
|
||||
+ }
|
||||
+
|
||||
+ #[inline]
|
||||
+ fn controller(&'a self) -> &'a C {
|
||||
+ self.client_ctrl.controller()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtapHID<'a, 'b, C> {
|
||||
+ fn enable(&'a self) {
|
||||
+ // Set up the default control endpoint
|
||||
+ self.client_ctrl.enable();
|
||||
+
|
||||
+ // Set up the interrupt in-out endpoint
|
||||
+ self.controller()
|
||||
+ .endpoint_set_in_buffer(1, &self.in_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_set_out_buffer(1, &self.out_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_in_out_enable(TransferType::Interrupt, 1);
|
||||
+ }
|
||||
+
|
||||
+ fn attach(&'a self) {
|
||||
+ self.client_ctrl.attach();
|
||||
+ }
|
||||
+
|
||||
+ fn bus_reset(&'a self) {
|
||||
+ // Should the client initiate reconfiguration here?
|
||||
+ // For now, the hardware layer does it.
|
||||
+
|
||||
+ debug!("Bus reset");
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control Setup transaction
|
||||
+ fn ctrl_setup(&'a self, endpoint: usize) -> hil::usb::CtrlSetupResult {
|
||||
+ self.client_ctrl.ctrl_setup(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control In transaction
|
||||
+ fn ctrl_in(&'a self, endpoint: usize) -> hil::usb::CtrlInResult {
|
||||
+ self.client_ctrl.ctrl_in(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Control Out transaction
|
||||
+ fn ctrl_out(&'a self, endpoint: usize, packet_bytes: u32) -> hil::usb::CtrlOutResult {
|
||||
+ self.client_ctrl.ctrl_out(endpoint, packet_bytes)
|
||||
+ }
|
||||
+
|
||||
+ fn ctrl_status(&'a self, endpoint: usize) {
|
||||
+ self.client_ctrl.ctrl_status(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle the completion of a Control transfer
|
||||
+ fn ctrl_status_complete(&'a self, endpoint: usize) {
|
||||
+ self.client_ctrl.ctrl_status_complete(endpoint)
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Bulk/Interrupt IN transaction
|
||||
+ fn packet_in(&'a self, transfer_type: TransferType, endpoint: usize) -> hil::usb::InResult {
|
||||
+ match transfer_type {
|
||||
+ TransferType::Bulk => hil::usb::InResult::Error,
|
||||
+ TransferType::Interrupt => {
|
||||
+ if endpoint != 1 {
|
||||
+ return hil::usb::InResult::Error;
|
||||
+ }
|
||||
+
|
||||
+ if let Some(packet) = self.tx_packet.take() {
|
||||
+ let buf = &self.in_buffer.buf;
|
||||
+ for i in 0..64 {
|
||||
+ buf[i].set(packet[i]);
|
||||
+ }
|
||||
+
|
||||
+ hil::usb::InResult::Packet(64)
|
||||
+ } else {
|
||||
+ // Nothing to send
|
||||
+ hil::usb::InResult::Delay
|
||||
+ }
|
||||
+ }
|
||||
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ /// Handle a Bulk/Interrupt OUT transaction
|
||||
+ fn packet_out(
|
||||
+ &'a self,
|
||||
+ transfer_type: TransferType,
|
||||
+ endpoint: usize,
|
||||
+ packet_bytes: u32,
|
||||
+ ) -> hil::usb::OutResult {
|
||||
+ match transfer_type {
|
||||
+ TransferType::Bulk => hil::usb::OutResult::Error,
|
||||
+ TransferType::Interrupt => {
|
||||
+ if endpoint != 1 {
|
||||
+ return hil::usb::OutResult::Error;
|
||||
+ }
|
||||
+
|
||||
+ if packet_bytes != 64 {
|
||||
+ // Cannot process this packet
|
||||
+ hil::usb::OutResult::Error
|
||||
+ } else {
|
||||
+ if self.send_packet_to_client() {
|
||||
+ hil::usb::OutResult::Ok
|
||||
+ } else {
|
||||
+ hil::usb::OutResult::Delay
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
+ if endpoint != 1 {
|
||||
+ panic!("Unexpected transmission on ep {}", endpoint);
|
||||
+ }
|
||||
+
|
||||
+ if self.tx_packet.is_some() {
|
||||
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
+ }
|
||||
+ self.pending_in.set(false);
|
||||
+
|
||||
+ // Clear any pending packet on the receiving side.
|
||||
+ // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
+ // receive another packet.
|
||||
+ self.cancel_out_transaction();
|
||||
+
|
||||
+ // Notify the client
|
||||
+ self.client.map(|client| client.packet_transmitted());
|
||||
+ }
|
||||
+}
|
||||
1102
patches/tock/03-add-ctap-modules.patch
Normal file
1102
patches/tock/03-add-ctap-modules.patch
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
diff --git a/capsules/Cargo.toml b/capsules/Cargo.toml
|
||||
index 680fe32b2..4f757b93d 100644
|
||||
index 65301bcf1..dc70e98b1 100644
|
||||
--- a/capsules/Cargo.toml
|
||||
+++ b/capsules/Cargo.toml
|
||||
@@ -8,3 +8,6 @@ edition = "2018"
|
||||
@@ -8,3 +8,6 @@ edition = "2021"
|
||||
kernel = { path = "../kernel" }
|
||||
enum_primitive = { path = "../libraries/enum_primitive" }
|
||||
tickv = { path = "../libraries/tickv" }
|
||||
@@ -10,10 +10,10 @@ index 680fe32b2..4f757b93d 100644
|
||||
+[features]
|
||||
+vendor_hid = []
|
||||
diff --git a/capsules/src/usb/descriptors.rs b/capsules/src/usb/descriptors.rs
|
||||
index dea2dfed9..66a16fe87 100644
|
||||
index 67f708239..c2556d3a2 100644
|
||||
--- a/capsules/src/usb/descriptors.rs
|
||||
+++ b/capsules/src/usb/descriptors.rs
|
||||
@@ -414,13 +414,14 @@ impl DescriptorBuffer {
|
||||
@@ -415,13 +415,14 @@ impl DescriptorBuffer {
|
||||
/// example, if the interface descriptor list contains `[ID1, ID2, ID3]`,
|
||||
/// and the endpoint descriptors list is `[[ED1, ED2], [ED3, ED4, ED5],
|
||||
/// [ED6]]`, then the third interface descriptor (`ID3`) has one
|
||||
@@ -30,7 +30,7 @@ index dea2dfed9..66a16fe87 100644
|
||||
cdc_descriptor: Option<&[CdcInterfaceDescriptor]>,
|
||||
) -> (DeviceBuffer, DescriptorBuffer) {
|
||||
// Create device descriptor buffer and fill.
|
||||
@@ -504,7 +505,7 @@ pub fn create_descriptor_buffers(
|
||||
@@ -505,7 +506,7 @@ pub fn create_descriptor_buffers(
|
||||
.iter()
|
||||
.map(|descs| descs.iter().map(|d| d.size()).sum::<usize>())
|
||||
.sum::<usize>()
|
||||
@@ -39,7 +39,7 @@ index dea2dfed9..66a16fe87 100644
|
||||
+ cdc_descriptor.map_or(0, |ds| ds.iter().map(|d| d.size()).sum::<usize>());
|
||||
|
||||
// Set the number of endpoints for each interface descriptor.
|
||||
@@ -521,13 +522,11 @@ pub fn create_descriptor_buffers(
|
||||
@@ -522,12 +523,10 @@ pub fn create_descriptor_buffers(
|
||||
// Add the interface descriptor.
|
||||
len += d.write_to(&other_buf.buf[len..]);
|
||||
|
||||
@@ -49,20 +49,18 @@ index dea2dfed9..66a16fe87 100644
|
||||
- // HID descriptor, if any.
|
||||
- if let Some(dh) = hid_descriptor {
|
||||
- len += dh.write_to(&other_buf.buf[len..]);
|
||||
- }
|
||||
+ // HID descriptor, if present, for this interface.
|
||||
+ if let Some(dh) = hid_descriptor {
|
||||
+ if let Some(d) = dh.get(i) {
|
||||
+ len += d.write_to(&other_buf.buf[len..]);
|
||||
+ }
|
||||
+ if let Some(d) = dh.get(i) {
|
||||
+ len += d.write_to(&other_buf.buf[len..]);
|
||||
}
|
||||
}
|
||||
|
||||
// If there is a CDC descriptor array, we include
|
||||
diff --git a/capsules/src/usb/usbc_client_ctrl.rs b/capsules/src/usb/usbc_client_ctrl.rs
|
||||
index f7899d8c5..6956523c6 100644
|
||||
index c8e55a8a8..54c7c3bbb 100644
|
||||
--- a/capsules/src/usb/usbc_client_ctrl.rs
|
||||
+++ b/capsules/src/usb/usbc_client_ctrl.rs
|
||||
@@ -38,6 +38,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
|
||||
@@ -40,6 +40,12 @@ const DESCRIPTOR_BUFLEN: usize = 128;
|
||||
|
||||
const N_ENDPOINTS: usize = 3;
|
||||
|
||||
@@ -75,7 +73,7 @@ index f7899d8c5..6956523c6 100644
|
||||
/// Handler for USB control endpoint requests.
|
||||
pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
/// The USB hardware controller.
|
||||
@@ -64,12 +70,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
@@ -66,12 +72,12 @@ pub struct ClientCtrl<'a, 'b, U: 'a> {
|
||||
|
||||
/// An optional HID descriptor for the configuration. This can be requested
|
||||
/// separately. It must also be included in `other_descriptor_buffer` if it exists.
|
||||
@@ -90,7 +88,7 @@ index f7899d8c5..6956523c6 100644
|
||||
|
||||
/// Supported language (only one for now).
|
||||
language: &'b [u16; 1],
|
||||
@@ -104,8 +110,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
@@ -106,8 +112,8 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
controller: &'a U,
|
||||
device_descriptor_buffer: DeviceBuffer,
|
||||
other_descriptor_buffer: DescriptorBuffer,
|
||||
@@ -101,7 +99,7 @@ index f7899d8c5..6956523c6 100644
|
||||
language: &'b [u16; 1],
|
||||
strings: &'b [&'b str],
|
||||
) -> Self {
|
||||
@@ -331,28 +337,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
@@ -333,28 +339,39 @@ impl<'a, 'b, U: hil::usb::UsbController<'a>> ClientCtrl<'a, 'b, U> {
|
||||
descriptor_type,
|
||||
// TODO: use the descriptor index
|
||||
descriptor_index: _,
|
||||
@@ -155,163 +153,3 @@ index f7899d8c5..6956523c6 100644
|
||||
} else {
|
||||
hil::usb::CtrlSetupResult::ErrGeneric
|
||||
}
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index 642039120..abf224f97 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -44,21 +44,59 @@ static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
0xC0, // HID_EndCollection
|
||||
];
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
+ 0x06, 0x00, 0xFF, // HID_UsagePage ( VENDOR ),
|
||||
+ 0x09, 0x01, // HID_Usage ( Unused ),
|
||||
+ 0xA1, 0x01, // HID_Collection ( HID_Application ),
|
||||
+ 0x09, 0x20, // HID_Usage ( FIDO_USAGE_DATA_IN ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_INPUT_REPORT_BYTES ),
|
||||
+ 0x81, 0x02, // HID_Input ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0x09, 0x21, // HID_Usage ( FIDO_USAGE_DATA_OUT ),
|
||||
+ 0x15, 0x00, // HID_LogicalMin ( 0 ),
|
||||
+ 0x26, 0xFF, 0x00, // HID_LogicalMaxS ( 0xff ),
|
||||
+ 0x75, 0x08, // HID_ReportSize ( 8 ),
|
||||
+ 0x95, 0x40, // HID_ReportCount ( HID_OUTPUT_REPORT_BYTES ),
|
||||
+ 0x91, 0x02, // HID_Output ( HID_Data | HID_Absolute | HID_Variable ),
|
||||
+ 0xC0, // HID_EndCollection
|
||||
+];
|
||||
+
|
||||
static CTAP_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
desc: CTAP_REPORT_DESCRIPTOR,
|
||||
};
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_REPORT: ReportDescriptor<'static> = ReportDescriptor {
|
||||
+ desc: VENDOR_REPORT_DESCRIPTOR,
|
||||
+};
|
||||
+
|
||||
static HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
typ: DescriptorType::Report,
|
||||
len: CTAP_REPORT_DESCRIPTOR.len() as u16,
|
||||
}];
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_HID_SUB_DESCRIPTORS: &'static [HIDSubordinateDescriptor] = &[HIDSubordinateDescriptor {
|
||||
+ typ: DescriptorType::Report,
|
||||
+ len: VENDOR_REPORT_DESCRIPTOR.len() as u16,
|
||||
+}];
|
||||
+
|
||||
static HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
hid_class: 0x0110,
|
||||
country_code: HIDCountryCode::NotSupported,
|
||||
sub_descriptors: HID_SUB_DESCRIPTORS,
|
||||
};
|
||||
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
+ hid_class: 0x0110,
|
||||
+ country_code: HIDCountryCode::NotSupported,
|
||||
+ sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
|
||||
+};
|
||||
+
|
||||
pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
|
||||
@@ -82,6 +120,9 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
product_id: u16,
|
||||
strings: &'static [&'static str],
|
||||
) -> Self {
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ debug!("vendor_hid enabled.");
|
||||
+
|
||||
let interfaces: &mut [InterfaceDescriptor] = &mut [
|
||||
// Interface declared in the FIDO2 specification, section 8.1.8.1
|
||||
InterfaceDescriptor {
|
||||
@@ -90,9 +131,19 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
interface_protocol: 0x00,
|
||||
..InterfaceDescriptor::default()
|
||||
},
|
||||
+ // Vendor HID interface.
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ InterfaceDescriptor {
|
||||
+ interface_number: 1,
|
||||
+ interface_class: 0x03, // HID
|
||||
+ interface_subclass: 0x00,
|
||||
+ interface_protocol: 0x00,
|
||||
+ ..InterfaceDescriptor::default()
|
||||
+ },
|
||||
];
|
||||
|
||||
let endpoints: &[&[EndpointDescriptor]] = &[&[
|
||||
+ // 2 Endpoints for FIDO
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
ENDPOINT_NUM,
|
||||
@@ -110,8 +161,30 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
transfer_type: TransferType::Interrupt,
|
||||
max_packet_size: 64,
|
||||
interval: 5,
|
||||
- },
|
||||
- ]];
|
||||
+ },],
|
||||
+ // 2 Endpoints for FIDO
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &[
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM + 1,
|
||||
+ TransferDirection::HostToDevice,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ EndpointDescriptor {
|
||||
+ endpoint_address: EndpointAddress::new_const(
|
||||
+ ENDPOINT_NUM + 1,
|
||||
+ TransferDirection::DeviceToHost,
|
||||
+ ),
|
||||
+ transfer_type: TransferType::Interrupt,
|
||||
+ max_packet_size: 64,
|
||||
+ interval: 5,
|
||||
+ },
|
||||
+ ],
|
||||
+ ];
|
||||
|
||||
let (device_descriptor_buffer, other_descriptor_buffer) =
|
||||
descriptors::create_descriptor_buffers(
|
||||
@@ -130,17 +203,28 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
},
|
||||
interfaces,
|
||||
endpoints,
|
||||
- Some(&HID),
|
||||
+ Some(&[
|
||||
+ &HID,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_HID,
|
||||
+ ]),
|
||||
None, // No CDC descriptor array
|
||||
);
|
||||
-
|
||||
ClientCtapHID {
|
||||
client_ctrl: ClientCtrl::new(
|
||||
controller,
|
||||
device_descriptor_buffer,
|
||||
other_descriptor_buffer,
|
||||
- Some(&HID),
|
||||
- Some(&CTAP_REPORT),
|
||||
+ Some([
|
||||
+ &HID,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_HID,
|
||||
+ ]),
|
||||
+ Some([
|
||||
+ &CTAP_REPORT,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ &VENDOR_REPORT,
|
||||
+ ]),
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
@@ -1,338 +0,0 @@
|
||||
diff --git a/boards/components/src/firmware_protection.rs b/boards/components/src/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..58695af81
|
||||
--- /dev/null
|
||||
+++ b/boards/components/src/firmware_protection.rs
|
||||
@@ -0,0 +1,70 @@
|
||||
+//! Component for firmware protection syscall interface.
|
||||
+//!
|
||||
+//! This provides one Component, `FirmwareProtectionComponent`, which implements a
|
||||
+//! userspace syscall interface to enable the code readout protection.
|
||||
+//!
|
||||
+//! Usage
|
||||
+//! -----
|
||||
+//! ```rust
|
||||
+//! let crp = components::firmware_protection::FirmwareProtectionComponent::new(
|
||||
+//! board_kernel,
|
||||
+//! nrf52840::uicr::Uicr::new()
|
||||
+//! )
|
||||
+//! .finalize(
|
||||
+//! components::firmware_protection_component_helper!(uicr));
|
||||
+//! ```
|
||||
+
|
||||
+use core::mem::MaybeUninit;
|
||||
+
|
||||
+use capsules::firmware_protection;
|
||||
+use kernel::capabilities;
|
||||
+use kernel::component::Component;
|
||||
+use kernel::create_capability;
|
||||
+use kernel::hil;
|
||||
+use kernel::static_init_half;
|
||||
+
|
||||
+// Setup static space for the objects.
|
||||
+#[macro_export]
|
||||
+macro_rules! firmware_protection_component_helper {
|
||||
+ ($C:ty) => {{
|
||||
+ use capsules::firmware_protection;
|
||||
+ use core::mem::MaybeUninit;
|
||||
+ static mut BUF: MaybeUninit<firmware_protection::FirmwareProtection<$C>> =
|
||||
+ MaybeUninit::uninit();
|
||||
+ &mut BUF
|
||||
+ };};
|
||||
+}
|
||||
+
|
||||
+pub struct FirmwareProtectionComponent<C: hil::firmware_protection::FirmwareProtection> {
|
||||
+ board_kernel: &'static kernel::Kernel,
|
||||
+ crp: C,
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> FirmwareProtectionComponent<C> {
|
||||
+ pub fn new(board_kernel: &'static kernel::Kernel, crp: C) -> FirmwareProtectionComponent<C> {
|
||||
+ FirmwareProtectionComponent {
|
||||
+ board_kernel: board_kernel,
|
||||
+ crp: crp,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: 'static + hil::firmware_protection::FirmwareProtection> Component
|
||||
+ for FirmwareProtectionComponent<C>
|
||||
+{
|
||||
+ type StaticInput = &'static mut MaybeUninit<firmware_protection::FirmwareProtection<C>>;
|
||||
+ type Output = &'static firmware_protection::FirmwareProtection<C>;
|
||||
+
|
||||
+ unsafe fn finalize(self, static_buffer: Self::StaticInput) -> Self::Output {
|
||||
+ let grant_cap = create_capability!(capabilities::MemoryAllocationCapability);
|
||||
+
|
||||
+ static_init_half!(
|
||||
+ static_buffer,
|
||||
+ firmware_protection::FirmwareProtection<C>,
|
||||
+ firmware_protection::FirmwareProtection::new(
|
||||
+ self.crp,
|
||||
+ self.board_kernel.create_grant(&grant_cap),
|
||||
+ )
|
||||
+ )
|
||||
+ }
|
||||
+}
|
||||
diff --git a/boards/components/src/lib.rs b/boards/components/src/lib.rs
|
||||
index a3bbe724b..95625f91f 100644
|
||||
--- a/boards/components/src/lib.rs
|
||||
+++ b/boards/components/src/lib.rs
|
||||
@@ -13,6 +13,7 @@ pub mod crc;
|
||||
pub mod ctap;
|
||||
pub mod debug_queue;
|
||||
pub mod debug_writer;
|
||||
+pub mod firmware_protection;
|
||||
pub mod ft6x06;
|
||||
pub mod fxos8700;
|
||||
pub mod gpio;
|
||||
diff --git a/capsules/src/driver.rs b/capsules/src/driver.rs
|
||||
index b6124c4c0..90a18ea47 100644
|
||||
--- a/capsules/src/driver.rs
|
||||
+++ b/capsules/src/driver.rs
|
||||
@@ -16,6 +16,7 @@ pub enum NUM {
|
||||
Adc = 0x00005,
|
||||
Dac = 0x00006,
|
||||
AnalogComparator = 0x00007,
|
||||
+ FirmwareProtection = 0x00008,
|
||||
|
||||
// Kernel
|
||||
Ipc = 0x10000,
|
||||
diff --git a/capsules/src/firmware_protection.rs b/capsules/src/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..8cf63d6a4
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/firmware_protection.rs
|
||||
@@ -0,0 +1,85 @@
|
||||
+//! Provides userspace control of firmware protection on a board.
|
||||
+//!
|
||||
+//! This allows an application to enable firware readout protection,
|
||||
+//! disabling JTAG interface and other ways to read/tamper the firmware.
|
||||
+//! Of course, outside of a hardware bug, once set, the only way to enable
|
||||
+//! programming/debugging is by fully erasing the flash.
|
||||
+//!
|
||||
+//! Usage
|
||||
+//! -----
|
||||
+//!
|
||||
+//! ```rust
|
||||
+//! # use kernel::static_init;
|
||||
+//!
|
||||
+//! let crp = static_init!(
|
||||
+//! capsules::firmware_protection::FirmwareProtection,
|
||||
+//! capsules::firmware_protection::FirmwareProtection::new(
|
||||
+//! nrf52840::uicr::Uicr,
|
||||
+//! board_kernel.create_grant(&grant_cap),
|
||||
+//! );
|
||||
+//! ```
|
||||
+//!
|
||||
+//! Syscall Interface
|
||||
+//! -----------------
|
||||
+//!
|
||||
+//! - Stability: 0 - Draft
|
||||
+//!
|
||||
+//! ### Command
|
||||
+//!
|
||||
+//! Enable code readout protection on the current board.
|
||||
+//!
|
||||
+//! #### `command_num`
|
||||
+//!
|
||||
+//! - `0`: Driver check.
|
||||
+//! - `1`: Get current firmware readout protection (aka CRP) state.
|
||||
+//! - `2`: Set current firmware readout protection (aka CRP) state.
|
||||
+//!
|
||||
+
|
||||
+use kernel::hil;
|
||||
+use kernel::{AppId, Callback, Driver, Grant, ReturnCode};
|
||||
+
|
||||
+/// Syscall driver number.
|
||||
+use crate::driver;
|
||||
+pub const DRIVER_NUM: usize = driver::NUM::FirmwareProtection as usize;
|
||||
+
|
||||
+pub struct FirmwareProtection<C: hil::firmware_protection::FirmwareProtection> {
|
||||
+ crp_unit: C,
|
||||
+ apps: Grant<Option<Callback>>,
|
||||
+}
|
||||
+
|
||||
+impl<C: hil::firmware_protection::FirmwareProtection> FirmwareProtection<C> {
|
||||
+ pub fn new(crp_unit: C, apps: Grant<Option<Callback>>) -> Self {
|
||||
+ Self { crp_unit, apps }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl<C: hil::firmware_protection::FirmwareProtection> Driver for FirmwareProtection<C> {
|
||||
+ ///
|
||||
+ /// ### Command numbers
|
||||
+ ///
|
||||
+ /// * `0`: Returns non-zero to indicate the driver is present.
|
||||
+ /// * `1`: Gets firmware protection state.
|
||||
+ /// * `2`: Sets firmware protection state.
|
||||
+ fn command(&self, command_num: usize, data: usize, _: usize, appid: AppId) -> ReturnCode {
|
||||
+ match command_num {
|
||||
+ // return if driver is available
|
||||
+ 0 => ReturnCode::SUCCESS,
|
||||
+
|
||||
+ 1 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |_, _| ReturnCode::SuccessWithValue {
|
||||
+ value: self.crp_unit.get_protection() as usize,
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+
|
||||
+ // sets firmware protection
|
||||
+ 2 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |_, _| self.crp_unit.set_protection(data.into()))
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+
|
||||
+ // default
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/lib.rs b/capsules/src/lib.rs
|
||||
index 925b4ba41..919638c5d 100644
|
||||
--- a/capsules/src/lib.rs
|
||||
+++ b/capsules/src/lib.rs
|
||||
@@ -25,6 +25,7 @@ pub mod ctap;
|
||||
pub mod dac;
|
||||
pub mod debug_process_restart;
|
||||
pub mod driver;
|
||||
+pub mod firmware_protection;
|
||||
pub mod fm25cl;
|
||||
pub mod ft6x06;
|
||||
pub mod fxos8700cq;
|
||||
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
|
||||
index 788533210..655a98d5b 100644
|
||||
--- a/chips/nrf52/src/uicr.rs
|
||||
+++ b/chips/nrf52/src/uicr.rs
|
||||
@@ -1,12 +1,14 @@
|
||||
//! User information configuration registers
|
||||
|
||||
use enum_primitive::cast::FromPrimitive;
|
||||
+use hil::firmware_protection::ProtectionLevel;
|
||||
use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
use kernel::hil;
|
||||
use kernel::ReturnCode;
|
||||
|
||||
use crate::gpio::Pin;
|
||||
+use crate::nvmc;
|
||||
|
||||
const UICR_BASE: StaticRef<UicrRegisters> =
|
||||
unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
|
||||
@@ -217,3 +219,49 @@ impl Uicr {
|
||||
self.registers.approtect.write(ApProtect::PALL::ENABLED);
|
||||
}
|
||||
}
|
||||
+
|
||||
+impl hil::firmware_protection::FirmwareProtection for Uicr {
|
||||
+ fn get_protection(&self) -> ProtectionLevel {
|
||||
+ let ap_protect_state = self.is_ap_protect_enabled();
|
||||
+ let cpu_debug_state = self
|
||||
+ .registers
|
||||
+ .debugctrl
|
||||
+ .matches_all(DebugControl::CPUNIDEN::ENABLED + DebugControl::CPUFPBEN::ENABLED);
|
||||
+ match (ap_protect_state, cpu_debug_state) {
|
||||
+ (false, _) => ProtectionLevel::NoProtection,
|
||||
+ (true, true) => ProtectionLevel::JtagDisabled,
|
||||
+ (true, false) => ProtectionLevel::FullyLocked,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode {
|
||||
+ let current_level = self.get_protection();
|
||||
+ if current_level > level || level == ProtectionLevel::Unknown {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ if current_level == level {
|
||||
+ return ReturnCode::EALREADY;
|
||||
+ }
|
||||
+
|
||||
+ nvmc::Nvmc::new().configure_writeable();
|
||||
+ if level >= ProtectionLevel::JtagDisabled {
|
||||
+ self.set_ap_protect();
|
||||
+ }
|
||||
+
|
||||
+ if level >= ProtectionLevel::FullyLocked {
|
||||
+ // Prevent CPU debug and flash patching. Leaving these enabled could
|
||||
+ // allow to circumvent protection.
|
||||
+ self.registers
|
||||
+ .debugctrl
|
||||
+ .write(DebugControl::CPUNIDEN::DISABLED + DebugControl::CPUFPBEN::DISABLED);
|
||||
+ // TODO(jmichel): prevent returning into bootloader if present
|
||||
+ }
|
||||
+ nvmc::Nvmc::new().configure_readonly();
|
||||
+
|
||||
+ if self.get_protection() == level {
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ ReturnCode::FAIL
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/hil/firmware_protection.rs b/kernel/src/hil/firmware_protection.rs
|
||||
new file mode 100644
|
||||
index 000000000..de0824646
|
||||
--- /dev/null
|
||||
+++ b/kernel/src/hil/firmware_protection.rs
|
||||
@@ -0,0 +1,48 @@
|
||||
+//! Interface for Firmware Protection, also called Code Readout Protection.
|
||||
+
|
||||
+use crate::returncode::ReturnCode;
|
||||
+
|
||||
+#[derive(PartialOrd, PartialEq)]
|
||||
+pub enum ProtectionLevel {
|
||||
+ /// Unsupported feature
|
||||
+ Unknown = 0,
|
||||
+ /// This should be the factory default for the chip.
|
||||
+ NoProtection = 1,
|
||||
+ /// At this level, only JTAG/SWD are disabled but other debugging
|
||||
+ /// features may still be enabled.
|
||||
+ JtagDisabled = 2,
|
||||
+ /// This is the maximum level of protection the chip supports.
|
||||
+ /// At this level, JTAG and all other features are expected to be
|
||||
+ /// disabled and only a full chip erase may allow to recover from
|
||||
+ /// that state.
|
||||
+ FullyLocked = 0xff,
|
||||
+}
|
||||
+
|
||||
+impl From<usize> for ProtectionLevel {
|
||||
+ fn from(value: usize) -> Self {
|
||||
+ match value {
|
||||
+ 1 => ProtectionLevel::NoProtection,
|
||||
+ 2 => ProtectionLevel::JtagDisabled,
|
||||
+ 0xff => ProtectionLevel::FullyLocked,
|
||||
+ _ => ProtectionLevel::Unknown,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub trait FirmwareProtection {
|
||||
+ /// Gets the current firmware protection level.
|
||||
+ /// This doesn't fail and always returns a value.
|
||||
+ fn get_protection(&self) -> ProtectionLevel;
|
||||
+
|
||||
+ /// Sets the firmware protection level.
|
||||
+ /// There are four valid return values:
|
||||
+ /// - SUCCESS: protection level has been set to `level`
|
||||
+ /// - FAIL: something went wrong while setting the protection
|
||||
+ /// level and the effective protection level is not the one
|
||||
+ /// that was requested.
|
||||
+ /// - EALREADY: the requested protection level is already the
|
||||
+ /// level that is set.
|
||||
+ /// - EINVAL: unsupported protection level or the requested
|
||||
+ /// protection level is lower than the currently set one.
|
||||
+ fn set_protection(&self, level: ProtectionLevel) -> ReturnCode;
|
||||
+}
|
||||
diff --git a/kernel/src/hil/mod.rs b/kernel/src/hil/mod.rs
|
||||
index f2d3629a9..d6eb39d2d 100644
|
||||
--- a/kernel/src/hil/mod.rs
|
||||
+++ b/kernel/src/hil/mod.rs
|
||||
@@ -9,6 +9,7 @@ pub mod dac;
|
||||
pub mod digest;
|
||||
pub mod eic;
|
||||
pub mod entropy;
|
||||
+pub mod firmware_protection;
|
||||
pub mod flash;
|
||||
pub mod gpio;
|
||||
pub mod gpio_async;
|
||||
24
patches/tock/05-kernel-utility-method.patch
Normal file
24
patches/tock/05-kernel-utility-method.patch
Normal file
@@ -0,0 +1,24 @@
|
||||
diff --git a/kernel/src/syscall_driver.rs b/kernel/src/syscall_driver.rs
|
||||
index 0e2943fe4..3b5b42486 100644
|
||||
--- a/kernel/src/syscall_driver.rs
|
||||
+++ b/kernel/src/syscall_driver.rs
|
||||
@@ -102,6 +102,19 @@ impl CommandReturn {
|
||||
self.0
|
||||
}
|
||||
|
||||
+ /// Check whether the inner `SyscallReturn` value is successful
|
||||
+ pub fn is_success(&self) -> bool {
|
||||
+ matches!(
|
||||
+ self.0,
|
||||
+ SyscallReturn::Success
|
||||
+ | SyscallReturn::SuccessU32(_)
|
||||
+ | SyscallReturn::SuccessU32U32(_, _)
|
||||
+ | SyscallReturn::SuccessU32U32U32(_, _, _)
|
||||
+ | SyscallReturn::SuccessU32U64(_, _)
|
||||
+ | SyscallReturn::SuccessU64(_)
|
||||
+ )
|
||||
+ }
|
||||
+
|
||||
/// Command error
|
||||
pub fn failure(rc: ErrorCode) -> Self {
|
||||
CommandReturn(SyscallReturn::Failure(rc))
|
||||
@@ -1,43 +1,53 @@
|
||||
diff --git a/chips/nrf52/src/nvmc.rs b/chips/nrf52/src/nvmc.rs
|
||||
index adbc2a2b5..4092cf346 100644
|
||||
index 61e94260e..b7c3be3f6 100644
|
||||
--- a/chips/nrf52/src/nvmc.rs
|
||||
+++ b/chips/nrf52/src/nvmc.rs
|
||||
@@ -3,15 +3,19 @@
|
||||
//! Used in order read and write to internal flash.
|
||||
|
||||
@@ -5,7 +5,14 @@
|
||||
use core::cell::Cell;
|
||||
+use core::convert::TryFrom;
|
||||
use core::ops::{Index, IndexMut};
|
||||
use kernel::common::cells::OptionalCell;
|
||||
use kernel::common::cells::TakeCell;
|
||||
use kernel::common::cells::VolatileCell;
|
||||
use kernel::common::deferred_call::DeferredCall;
|
||||
+use kernel::common::dynamic_deferred_call::{
|
||||
use kernel::deferred_call::DeferredCall;
|
||||
+use kernel::dynamic_deferred_call::{
|
||||
+ DeferredCallHandle, DynamicDeferredCall, DynamicDeferredCallClient,
|
||||
+};
|
||||
use kernel::common::registers::{register_bitfields, ReadOnly, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
+use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
|
||||
use kernel::hil;
|
||||
-use kernel::ReturnCode;
|
||||
+use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
|
||||
use crate::deferred_call_tasks::DeferredCallTask;
|
||||
|
||||
@@ -141,7 +145,13 @@ register_bitfields! [u32,
|
||||
+use kernel::process::ProcessId;
|
||||
+use kernel::processbuffer::ReadableProcessBuffer;
|
||||
+use kernel::syscall::CommandReturn;
|
||||
use kernel::utilities::cells::OptionalCell;
|
||||
use kernel::utilities::cells::TakeCell;
|
||||
use kernel::utilities::cells::VolatileCell;
|
||||
@@ -142,7 +149,13 @@ register_bitfields! [u32,
|
||||
static DEFERRED_CALL: DeferredCall<DeferredCallTask> =
|
||||
unsafe { DeferredCall::new(DeferredCallTask::Nvmc) };
|
||||
|
||||
+type WORD = u32;
|
||||
+const WORD_SIZE: usize = core::mem::size_of::<WORD>();
|
||||
const PAGE_SIZE: usize = 4096;
|
||||
+const MAX_WORD_WRITES: usize = 2;
|
||||
+const MAX_PAGE_ERASES: usize = 10000;
|
||||
+const MAX_WORD_WRITES: u32 = 2;
|
||||
+const MAX_PAGE_ERASES: u32 = 10_000;
|
||||
+const WORD_MASK: usize = WORD_SIZE - 1;
|
||||
+const PAGE_MASK: usize = PAGE_SIZE - 1;
|
||||
|
||||
/// This is a wrapper around a u8 array that is sized to a single page for the
|
||||
/// nrf. Users of this module must pass an object of this type to use the
|
||||
@@ -217,6 +227,10 @@ impl Nvmc {
|
||||
@@ -157,13 +170,11 @@ const PAGE_SIZE: usize = 4096;
|
||||
///
|
||||
/// let pagebuffer = unsafe { static_init!(NrfPage, NrfPage::default()) };
|
||||
/// ```
|
||||
-pub struct NrfPage(pub [u8; PAGE_SIZE as usize]);
|
||||
+pub struct NrfPage(pub [u8; PAGE_SIZE]);
|
||||
|
||||
impl Default for NrfPage {
|
||||
fn default() -> Self {
|
||||
- Self {
|
||||
- 0: [0; PAGE_SIZE as usize],
|
||||
- }
|
||||
+ Self { 0: [0; PAGE_SIZE] }
|
||||
}
|
||||
}
|
||||
impl NrfPage {
|
||||
@@ -218,6 +229,10 @@ impl Nvmc {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,18 +58,15 @@ index adbc2a2b5..4092cf346 100644
|
||||
/// Configure the NVMC to allow writes to flash.
|
||||
pub fn configure_writeable(&self) {
|
||||
self.registers.config.write(Configuration::WEN::Wen);
|
||||
@@ -229,9 +243,7 @@ impl Nvmc {
|
||||
pub fn erase_uicr(&self) {
|
||||
self.registers.config.write(Configuration::WEN::Een);
|
||||
while !self.is_ready() {}
|
||||
- self.registers
|
||||
- .erasepage
|
||||
- .write(ErasePage::ERASEPAGE.val(0x10001000));
|
||||
@@ -233,6 +248,7 @@ impl Nvmc {
|
||||
self.registers
|
||||
.erasepage
|
||||
.write(ErasePage::ERASEPAGE.val(0x10001000));
|
||||
+ self.registers.eraseuicr.write(EraseUicr::ERASEUICR::ERASE);
|
||||
while !self.is_ready() {}
|
||||
}
|
||||
|
||||
@@ -319,7 +331,7 @@ impl Nvmc {
|
||||
@@ -320,7 +336,7 @@ impl Nvmc {
|
||||
// Put the NVMC in write mode.
|
||||
self.registers.config.write(Configuration::WEN::Wen);
|
||||
|
||||
@@ -68,11 +75,13 @@ index adbc2a2b5..4092cf346 100644
|
||||
let word: u32 = (data[i + 0] as u32) << 0
|
||||
| (data[i + 1] as u32) << 8
|
||||
| (data[i + 2] as u32) << 16
|
||||
@@ -387,3 +399,237 @@ impl hil::flash::Flash for Nvmc {
|
||||
@@ -388,3 +404,228 @@ impl hil::flash::Flash for Nvmc {
|
||||
self.erase_page(page_number)
|
||||
}
|
||||
}
|
||||
+
|
||||
+type NvmcDriverGrant = Grant<App, UpcallCount<1>, AllowRoCount<1>, AllowRwCount<0>>;
|
||||
+
|
||||
+/// Provides access to the writeable flash regions of the application.
|
||||
+///
|
||||
+/// The purpose of this driver is to provide low-level access to the embedded flash of nRF52 boards
|
||||
@@ -112,21 +121,17 @@ index adbc2a2b5..4092cf346 100644
|
||||
+/// - ALLOW(0): The allow slice for COMMAND(2).
|
||||
+pub struct SyscallDriver {
|
||||
+ nvmc: &'static Nvmc,
|
||||
+ apps: Grant<App>,
|
||||
+ waiting: OptionalCell<AppId>,
|
||||
+ apps: NvmcDriverGrant,
|
||||
+ waiting: OptionalCell<ProcessId>,
|
||||
+ deferred_caller: &'static DynamicDeferredCall,
|
||||
+ deferred_handle: OptionalCell<DeferredCallHandle>,
|
||||
+ buffer: TakeCell<'static, [u8]>,
|
||||
+}
|
||||
+
|
||||
+pub const DRIVER_NUM: usize = 0x50003;
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ /// The callback for COMMAND(2) and COMMAND(3).
|
||||
+ callback: Option<Callback>,
|
||||
+ /// The allow slice for COMMAND(2).
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+}
|
||||
+pub struct App {}
|
||||
+
|
||||
+fn is_write_needed(old: u32, new: u32) -> bool {
|
||||
+ // No need to write if it would not modify the current value.
|
||||
@@ -136,8 +141,9 @@ index adbc2a2b5..4092cf346 100644
|
||||
+impl SyscallDriver {
|
||||
+ pub fn new(
|
||||
+ nvmc: &'static Nvmc,
|
||||
+ apps: Grant<App>,
|
||||
+ apps: NvmcDriverGrant,
|
||||
+ deferred_caller: &'static DynamicDeferredCall,
|
||||
+ buffer: &'static mut [u8],
|
||||
+ ) -> SyscallDriver {
|
||||
+ nvmc.configure_readonly();
|
||||
+ SyscallDriver {
|
||||
@@ -146,6 +152,7 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ waiting: OptionalCell::empty(),
|
||||
+ deferred_caller,
|
||||
+ deferred_handle: OptionalCell::empty(),
|
||||
+ buffer: TakeCell::new(buffer),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
@@ -171,9 +178,9 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ /// - `ptr` must be word-aligned.
|
||||
+ /// - `slice.len()` must be word-aligned.
|
||||
+ /// - The slice starting at `ptr` of length `slice.len()` must fit in the storage.
|
||||
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> ReturnCode {
|
||||
+ fn write_slice(&self, ptr: usize, slice: &[u8]) -> CommandReturn {
|
||||
+ if ptr & WORD_MASK != 0 || slice.len() & WORD_MASK != 0 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ self.nvmc.configure_writeable();
|
||||
+ for (i, chunk) in slice.chunks(WORD_SIZE).enumerate() {
|
||||
@@ -188,7 +195,7 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ self.nvmc.configure_readonly();
|
||||
+ self.deferred_handle
|
||||
+ .map(|handle| self.deferred_caller.set(*handle));
|
||||
+ ReturnCode::SUCCESS
|
||||
+ CommandReturn::success()
|
||||
+ }
|
||||
+
|
||||
+ /// Erases a page at a page-aligned address.
|
||||
@@ -198,162 +205,249 @@ index adbc2a2b5..4092cf346 100644
|
||||
+ /// Fails with `EINVAL` if any of the following conditions does not hold:
|
||||
+ /// - `ptr` must be page-aligned.
|
||||
+ /// - The slice starting at `ptr` of length `PAGE_SIZE` must fit in the storage.
|
||||
+ fn erase_page(&self, ptr: usize) -> ReturnCode {
|
||||
+ fn erase_page(&self, ptr: usize) -> CommandReturn {
|
||||
+ if ptr & PAGE_MASK != 0 {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ self.nvmc.erase_page_helper(ptr / PAGE_SIZE);
|
||||
+ self.nvmc.configure_readonly();
|
||||
+ self.deferred_handle
|
||||
+ .map(|handle| self.deferred_caller.set(*handle));
|
||||
+ ReturnCode::SUCCESS
|
||||
+ CommandReturn::success()
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl DynamicDeferredCallClient for SyscallDriver {
|
||||
+ fn call(&self, _handle: DeferredCallHandle) {
|
||||
+ self.waiting.take().map(|appid| {
|
||||
+ self.apps.enter(appid, |app, _| {
|
||||
+ app.callback.map(|mut cb| {
|
||||
+ cb.schedule(0, 0, 0);
|
||||
+ });
|
||||
+ self.waiting.take().map(|process_id| {
|
||||
+ self.apps.enter(process_id, |_, kernel_data| {
|
||||
+ kernel_data.schedule_upcall(0, (0, 0, 0))
|
||||
+ })
|
||||
+ });
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+impl Driver for SyscallDriver {
|
||||
+ fn subscribe(
|
||||
+ &self,
|
||||
+ subscribe_num: usize,
|
||||
+ callback: Option<Callback>,
|
||||
+ appid: AppId,
|
||||
+ ) -> ReturnCode {
|
||||
+ match subscribe_num {
|
||||
+ 0 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ app.callback = callback;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ }
|
||||
+impl kernel::syscall::SyscallDriver for SyscallDriver {
|
||||
+ fn allocate_grant(&self, process_id: ProcessId) -> Result<(), kernel::process::Error> {
|
||||
+ self.apps.enter(process_id, |_, _| {})
|
||||
+ }
|
||||
+
|
||||
+ fn command(&self, cmd: usize, arg0: usize, arg1: usize, appid: AppId) -> ReturnCode {
|
||||
+ match (cmd, arg0, arg1) {
|
||||
+ (0, _, _) => ReturnCode::SUCCESS,
|
||||
+ fn command(
|
||||
+ &self,
|
||||
+ command_num: usize,
|
||||
+ r2: usize,
|
||||
+ r3: usize,
|
||||
+ process_id: ProcessId,
|
||||
+ ) -> CommandReturn {
|
||||
+ match (command_num, r2, r3) {
|
||||
+ (0, _, _) => CommandReturn::success(),
|
||||
+
|
||||
+ (1, 0, _) => ReturnCode::SuccessWithValue { value: WORD_SIZE },
|
||||
+ (1, 1, _) => ReturnCode::SuccessWithValue { value: PAGE_SIZE },
|
||||
+ (1, 2, _) => ReturnCode::SuccessWithValue {
|
||||
+ value: MAX_WORD_WRITES,
|
||||
+ },
|
||||
+ (1, 3, _) => ReturnCode::SuccessWithValue {
|
||||
+ value: MAX_PAGE_ERASES,
|
||||
+ },
|
||||
+ (1, _, _) => ReturnCode::EINVAL,
|
||||
+ (1, 0, _) => CommandReturn::success_u32(WORD_SIZE.try_into().unwrap()),
|
||||
+ (1, 1, _) => CommandReturn::success_u32(PAGE_SIZE.try_into().unwrap()),
|
||||
+ (1, 2, _) => CommandReturn::success_u32(MAX_WORD_WRITES),
|
||||
+ (1, 3, _) => CommandReturn::success_u32(MAX_PAGE_ERASES),
|
||||
+ (1, _, _) => CommandReturn::failure(ErrorCode::INVAL),
|
||||
+
|
||||
+ (2, ptr, len) => self
|
||||
+ (2, ptr, _len) => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ let slice = match app.slice.take() {
|
||||
+ None => return ReturnCode::EINVAL,
|
||||
+ Some(slice) => slice,
|
||||
+ };
|
||||
+ if len != slice.len() {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ }
|
||||
+ if self.waiting.is_some() {
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ }
|
||||
+ self.waiting.set(appid);
|
||||
+ self.write_slice(ptr, slice.as_ref())
|
||||
+ .enter(process_id, |_, kernel| {
|
||||
+ kernel
|
||||
+ .get_readonly_processbuffer(0)
|
||||
+ .and_then(|processbuffer| {
|
||||
+ processbuffer.enter(|app_buf| {
|
||||
+ // Copy contents to the internal buffer first
|
||||
+ self.buffer.take().map_or(
|
||||
+ CommandReturn::failure(ErrorCode::RESERVE),
|
||||
+ |buffer| {
|
||||
+ // as the drivers buffer can be bigger than the app buffer,
|
||||
+ // we choose the minimum to not copy anymore than we need
|
||||
+ let len = core::cmp::min(buffer.len(), app_buf.len());
|
||||
+
|
||||
+ // safety check when the app buffer is too large
|
||||
+ if app_buf.len() > buffer.len() {
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+
|
||||
+ let d = &app_buf[0..len];
|
||||
+ for (i, v) in buffer.as_mut()[0..len].iter_mut().enumerate()
|
||||
+ {
|
||||
+ *v = d[i].get();
|
||||
+ }
|
||||
+
|
||||
+ if self.waiting.is_some() {
|
||||
+ return CommandReturn::failure(ErrorCode::BUSY);
|
||||
+ }
|
||||
+ self.waiting.set(process_id);
|
||||
+ let result = self.write_slice(ptr, &buffer[0..len]);
|
||||
+ self.buffer.replace(buffer);
|
||||
+ result
|
||||
+ },
|
||||
+ )
|
||||
+ })
|
||||
+ })
|
||||
+ .unwrap_or(CommandReturn::failure(ErrorCode::RESERVE))
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ .unwrap_or_else(|e| CommandReturn::failure(e.into())),
|
||||
+
|
||||
+ (3, ptr, len) => {
|
||||
+ if len != PAGE_SIZE {
|
||||
+ return ReturnCode::EINVAL;
|
||||
+ return CommandReturn::failure(ErrorCode::INVAL);
|
||||
+ }
|
||||
+ if self.waiting.is_some() {
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ return CommandReturn::failure(ErrorCode::BUSY);
|
||||
+ }
|
||||
+ self.waiting.set(appid);
|
||||
+ self.waiting.set(process_id);
|
||||
+ self.erase_page(ptr)
|
||||
+ }
|
||||
+
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+ _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/kernel.rs b/kernel/src/kernel.rs
|
||||
index a1d081ad7..583fbe8d6 100644
|
||||
--- a/kernel/src/kernel.rs
|
||||
+++ b/kernel/src/kernel.rs
|
||||
@@ -38,6 +38,20 @@ use crate::utilities::cells::NumericCellExt;
|
||||
/// is less than this threshold.
|
||||
pub(crate) const MIN_QUANTA_THRESHOLD_US: u32 = 500;
|
||||
|
||||
+/// Represents the type of a storage slice.
|
||||
+#[derive(Copy, Clone)]
|
||||
+pub enum StorageType {
|
||||
+ Store = 1,
|
||||
+ Partition = 2,
|
||||
+}
|
||||
+
|
||||
+ fn allow(
|
||||
+ &self,
|
||||
+ appid: AppId,
|
||||
+ allow_num: usize,
|
||||
+ slice: Option<AppSlice<Shared, u8>>,
|
||||
+ ) -> ReturnCode {
|
||||
+ match allow_num {
|
||||
+ 0 => self
|
||||
+ .apps
|
||||
+ .enter(appid, |app, _| {
|
||||
+ app.slice = slice;
|
||||
+ ReturnCode::SUCCESS
|
||||
+ })
|
||||
+ .unwrap_or_else(|err| err.into()),
|
||||
+ _ => ReturnCode::ENOSUPPORT,
|
||||
+/// Represents a storage location in flash.
|
||||
+pub struct StorageLocation {
|
||||
+ pub address: usize,
|
||||
+ pub size: usize,
|
||||
+ pub storage_type: StorageType,
|
||||
+}
|
||||
+
|
||||
/// Main object for the kernel. Each board will need to create one.
|
||||
pub struct Kernel {
|
||||
/// How many "to-do" items exist at any given time. These include
|
||||
@@ -47,6 +61,9 @@ pub struct Kernel {
|
||||
/// This holds a pointer to the static array of Process pointers.
|
||||
processes: &'static [Option<&'static dyn process::Process>],
|
||||
|
||||
+ /// List of storage locations.
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+
|
||||
/// A counter which keeps track of how many process identifiers have been
|
||||
/// created. This is used to create new unique identifiers for processes.
|
||||
process_identifier_max: Cell<usize>,
|
||||
@@ -110,15 +127,26 @@ fn try_allocate_grant(driver: &dyn SyscallDriver, process: &dyn process::Process
|
||||
|
||||
impl Kernel {
|
||||
pub fn new(processes: &'static [Option<&'static dyn process::Process>]) -> Kernel {
|
||||
+ Kernel::new_with_storage(processes, &[])
|
||||
+ }
|
||||
+ pub fn new_with_storage(
|
||||
+ processes: &'static [Option<&'static dyn process::Process>],
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+ ) -> Kernel {
|
||||
Kernel {
|
||||
work: Cell::new(0),
|
||||
processes,
|
||||
+ storage_locations,
|
||||
process_identifier_max: Cell::new(0),
|
||||
grant_counter: Cell::new(0),
|
||||
grants_finalized: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
|
||||
+ self.storage_locations
|
||||
+ }
|
||||
+
|
||||
/// Something was scheduled for a process, so there is more work to do.
|
||||
///
|
||||
/// This is only exposed in the core kernel crate.
|
||||
@@ -1374,3 +1402,14 @@ impl Kernel {
|
||||
}
|
||||
}
|
||||
}
|
||||
+
|
||||
+impl TryFrom<StorageType> for u32 {
|
||||
+ type Error = ();
|
||||
+
|
||||
+ fn try_from(value: StorageType) -> Result<Self, Self::Error> {
|
||||
+ match value {
|
||||
+ StorageType::Store => Ok(StorageType::Store as u32),
|
||||
+ StorageType::Partition => Ok(StorageType::Partition as u32),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
|
||||
index 49e1e5182..807170195 100644
|
||||
index 028f30220..8880bc000 100644
|
||||
--- a/kernel/src/lib.rs
|
||||
+++ b/kernel/src/lib.rs
|
||||
@@ -122,7 +122,7 @@ pub use crate::sched::cooperative::{CoopProcessNode, CooperativeSched};
|
||||
pub use crate::sched::mlfq::{MLFQProcessNode, MLFQSched};
|
||||
pub use crate::sched::priority::PrioritySched;
|
||||
pub use crate::sched::round_robin::{RoundRobinProcessNode, RoundRobinSched};
|
||||
-pub use crate::sched::{Kernel, Scheduler};
|
||||
+pub use crate::sched::{Kernel, Scheduler, StorageLocation};
|
||||
|
||||
// Export only select items from the process module. To remove the name conflict
|
||||
// this cannot be called `process`, so we use a shortened version. These
|
||||
@@ -125,5 +125,6 @@ mod syscall_driver;
|
||||
// Core resources exposed as `kernel::Type`.
|
||||
pub use crate::errorcode::ErrorCode;
|
||||
pub use crate::kernel::Kernel;
|
||||
+pub use crate::kernel::{StorageLocation, StorageType};
|
||||
pub use crate::process::ProcessId;
|
||||
pub use crate::scheduler::Scheduler;
|
||||
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
|
||||
index 348c746a5..5465c95f4 100644
|
||||
index 51d89f37c..45ab3856b 100644
|
||||
--- a/kernel/src/memop.rs
|
||||
+++ b/kernel/src/memop.rs
|
||||
@@ -108,6 +108,25 @@ pub(crate) fn memop(process: &dyn ProcessType, op_type: usize, r1: usize) -> Ret
|
||||
ReturnCode::SUCCESS
|
||||
@@ -107,6 +107,37 @@ pub(crate) fn memop(process: &dyn Process, op_type: usize, r1: usize) -> Syscall
|
||||
SyscallReturn::Success
|
||||
}
|
||||
|
||||
+ // Op Type 12: Number of storage locations.
|
||||
+ 12 => ReturnCode::SuccessWithValue { value: process.number_storage_locations() },
|
||||
+ // 12 - 15 are required for the custom persistent store driver
|
||||
+ // currently only implemented in the nvmc module of nrf52 series
|
||||
+ // driver number: 0x50003
|
||||
+
|
||||
+ // Op Type 13: The start address of the storage location indexed by r1.
|
||||
+ // Op Type 12: Number of storage locations
|
||||
+ 12 => SyscallReturn::SuccessU32(process.number_storage_locations() as u32),
|
||||
+
|
||||
+ // Op Type 13: The start address of the storage location indexed by r1
|
||||
+ 13 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => ReturnCode::FAIL,
|
||||
+ Some(x) => ReturnCode::SuccessWithValue { value: x.address }
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.address as u32),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ // Op Type 14: The size of the storage location indexed by r1.
|
||||
+ 14 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => ReturnCode::FAIL,
|
||||
+ Some(x) => ReturnCode::SuccessWithValue { value: x.size }
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.size as u32),
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_ => ReturnCode::ENOSUPPORT,
|
||||
+ // Op Type 15: The type of the storage location indexed by r1.
|
||||
+ 15 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => SyscallReturn::Failure(ErrorCode::FAIL),
|
||||
+ Some(x) => SyscallReturn::SuccessU32(x.storage_type as u32)
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_ => SyscallReturn::Failure(ErrorCode::NOSUPPORT),
|
||||
}
|
||||
}
|
||||
diff --git a/kernel/src/process.rs b/kernel/src/process.rs
|
||||
index c52754be3..ae6a58341 100644
|
||||
index c1794d9bf..4fbad3f0b 100644
|
||||
--- a/kernel/src/process.rs
|
||||
+++ b/kernel/src/process.rs
|
||||
@@ -359,6 +359,15 @@ pub trait ProcessType {
|
||||
@@ -9,7 +9,7 @@ use core::str;
|
||||
use crate::capabilities;
|
||||
use crate::errorcode::ErrorCode;
|
||||
use crate::ipc;
|
||||
-use crate::kernel::Kernel;
|
||||
+use crate::kernel::{Kernel, StorageLocation};
|
||||
use crate::platform::mpu::{self};
|
||||
use crate::processbuffer::{ReadOnlyProcessBuffer, ReadWriteProcessBuffer};
|
||||
use crate::storage_permissions;
|
||||
@@ -335,6 +335,15 @@ pub trait Process {
|
||||
/// writeable flash region.
|
||||
fn get_writeable_flash_region(&self, region_index: usize) -> (u32, u32);
|
||||
|
||||
@@ -361,7 +455,7 @@ index c52754be3..ae6a58341 100644
|
||||
+ fn number_storage_locations(&self) -> usize;
|
||||
+
|
||||
+ /// Get the i-th storage location.
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation>;
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation>;
|
||||
+
|
||||
+ /// Whether a slice fits in a storage location.
|
||||
+ fn fits_in_storage_location(&self, ptr: usize, len: usize) -> bool;
|
||||
@@ -369,15 +463,28 @@ index c52754be3..ae6a58341 100644
|
||||
/// Debug function to update the kernel on where the stack starts for this
|
||||
/// process. Processes are not required to call this through the memop
|
||||
/// system call, but it aids in debugging the process.
|
||||
@@ -1048,6 +1057,35 @@ impl<C: Chip> ProcessType for Process<'_, C> {
|
||||
self.header.get_writeable_flash_region(region_index)
|
||||
diff --git a/kernel/src/process_standard.rs b/kernel/src/process_standard.rs
|
||||
index fa76e2c68..dd03e8f29 100644
|
||||
--- a/kernel/src/process_standard.rs
|
||||
+++ b/kernel/src/process_standard.rs
|
||||
@@ -14,7 +14,7 @@ use crate::collections::ring_buffer::RingBuffer;
|
||||
use crate::config;
|
||||
use crate::debug;
|
||||
use crate::errorcode::ErrorCode;
|
||||
-use crate::kernel::Kernel;
|
||||
+use crate::kernel::{Kernel, StorageLocation};
|
||||
use crate::platform::chip::Chip;
|
||||
use crate::platform::mpu::{self, MPU};
|
||||
use crate::process::{Error, FunctionCall, FunctionCallSource, Process, State, Task};
|
||||
@@ -258,6 +258,35 @@ impl<C: Chip> Process for ProcessStandard<'_, C> {
|
||||
ret
|
||||
}
|
||||
|
||||
+ fn number_storage_locations(&self) -> usize {
|
||||
+ self.kernel.storage_locations().len()
|
||||
+ }
|
||||
+
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&crate::StorageLocation> {
|
||||
+ fn get_storage_location(&self, index: usize) -> Option<&StorageLocation> {
|
||||
+ self.kernel.storage_locations().get(index)
|
||||
+ }
|
||||
+
|
||||
@@ -402,10 +509,10 @@ index c52754be3..ae6a58341 100644
|
||||
+ })
|
||||
+ }
|
||||
+
|
||||
fn update_stack_start_pointer(&self, stack_pointer: *const u8) {
|
||||
if stack_pointer >= self.mem_start() && stack_pointer < self.mem_end() {
|
||||
self.debug.map(|debug| {
|
||||
@@ -1751,6 +1789,33 @@ impl<C: 'static + Chip> Process<'_, C> {
|
||||
fn ready(&self) -> bool {
|
||||
self.tasks.map_or(false, |ring_buf| ring_buf.has_elements())
|
||||
|| self.state.get() == State::Running
|
||||
@@ -1366,6 +1395,33 @@ impl<C: 'static + Chip> ProcessStandard<'_, C> {
|
||||
return Err(ProcessLoadError::MpuInvalidFlashLength);
|
||||
}
|
||||
|
||||
@@ -436,60 +543,6 @@ index c52754be3..ae6a58341 100644
|
||||
+ return Ok((None, remaining_memory));
|
||||
+ }
|
||||
+
|
||||
// Determine how much space we need in the application's
|
||||
// memory space just for kernel and grant state. We need to make
|
||||
// sure we allocate enough memory just for that.
|
||||
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
|
||||
index 10626a2e1..8844bc6c3 100644
|
||||
--- a/kernel/src/sched.rs
|
||||
+++ b/kernel/src/sched.rs
|
||||
@@ -118,6 +118,12 @@ pub enum SchedulingDecision {
|
||||
TrySleep,
|
||||
}
|
||||
|
||||
+/// Represents a storage location in flash.
|
||||
+pub struct StorageLocation {
|
||||
+ pub address: usize,
|
||||
+ pub size: usize,
|
||||
+}
|
||||
+
|
||||
/// Main object for the kernel. Each board will need to create one.
|
||||
pub struct Kernel {
|
||||
/// How many "to-do" items exist at any given time. These include
|
||||
@@ -127,6 +133,9 @@ pub struct Kernel {
|
||||
/// This holds a pointer to the static array of Process pointers.
|
||||
processes: &'static [Option<&'static dyn process::ProcessType>],
|
||||
|
||||
+ /// List of storage locations.
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+
|
||||
/// A counter which keeps track of how many process identifiers have been
|
||||
/// created. This is used to create new unique identifiers for processes.
|
||||
process_identifier_max: Cell<usize>,
|
||||
@@ -170,9 +179,17 @@ pub enum StoppedExecutingReason {
|
||||
|
||||
impl Kernel {
|
||||
pub fn new(processes: &'static [Option<&'static dyn process::ProcessType>]) -> Kernel {
|
||||
+ Kernel::new_with_storage(processes, &[])
|
||||
+ }
|
||||
+
|
||||
+ pub fn new_with_storage(
|
||||
+ processes: &'static [Option<&'static dyn process::ProcessType>],
|
||||
+ storage_locations: &'static [StorageLocation],
|
||||
+ ) -> Kernel {
|
||||
Kernel {
|
||||
work: Cell::new(0),
|
||||
processes,
|
||||
+ storage_locations: storage_locations,
|
||||
process_identifier_max: Cell::new(0),
|
||||
grant_counter: Cell::new(0),
|
||||
grants_finalized: Cell::new(false),
|
||||
@@ -900,4 +917,8 @@ impl Kernel {
|
||||
|
||||
(return_reason, time_executed_us)
|
||||
}
|
||||
+
|
||||
+ pub fn storage_locations(&self) -> &'static [StorageLocation] {
|
||||
+ self.storage_locations
|
||||
+ }
|
||||
}
|
||||
// Determine how much space we need in the application's memory space
|
||||
// just for kernel and grant state. We need to make sure we allocate
|
||||
// enough memory just for that.
|
||||
@@ -1,55 +0,0 @@
|
||||
diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs
|
||||
index 807170195..a13413791 100644
|
||||
--- a/kernel/src/lib.rs
|
||||
+++ b/kernel/src/lib.rs
|
||||
@@ -122,7 +122,7 @@ pub use crate::sched::cooperative::{CoopProcessNode, CooperativeSched};
|
||||
pub use crate::sched::mlfq::{MLFQProcessNode, MLFQSched};
|
||||
pub use crate::sched::priority::PrioritySched;
|
||||
pub use crate::sched::round_robin::{RoundRobinProcessNode, RoundRobinSched};
|
||||
-pub use crate::sched::{Kernel, Scheduler, StorageLocation};
|
||||
+pub use crate::sched::{Kernel, Scheduler, StorageLocation, StorageType};
|
||||
|
||||
// Export only select items from the process module. To remove the name conflict
|
||||
// this cannot be called `process`, so we use a shortened version. These
|
||||
diff --git a/kernel/src/memop.rs b/kernel/src/memop.rs
|
||||
index 5465c95f4..e596648f7 100644
|
||||
--- a/kernel/src/memop.rs
|
||||
+++ b/kernel/src/memop.rs
|
||||
@@ -127,6 +127,14 @@ pub(crate) fn memop(process: &dyn ProcessType, op_type: usize, r1: usize) -> Ret
|
||||
}
|
||||
}
|
||||
|
||||
+ // Op Type 15: The type of the storage location indexed by r1.
|
||||
+ 15 => {
|
||||
+ match process.get_storage_location(r1) {
|
||||
+ None => ReturnCode::FAIL,
|
||||
+ Some(x) => ReturnCode::SuccessWithValue { value: x.storage_type as usize }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
_ => ReturnCode::ENOSUPPORT,
|
||||
}
|
||||
}
|
||||
diff --git a/kernel/src/sched.rs b/kernel/src/sched.rs
|
||||
index 8844bc6c3..00c13a7c6 100644
|
||||
--- a/kernel/src/sched.rs
|
||||
+++ b/kernel/src/sched.rs
|
||||
@@ -118,10 +118,18 @@ pub enum SchedulingDecision {
|
||||
TrySleep,
|
||||
}
|
||||
|
||||
+/// Represents the type of a storage slice.
|
||||
+#[derive(Copy, Clone)]
|
||||
+pub enum StorageType {
|
||||
+ Store = 1,
|
||||
+ Partition = 2,
|
||||
+}
|
||||
+
|
||||
/// Represents a storage location in flash.
|
||||
pub struct StorageLocation {
|
||||
pub address: usize,
|
||||
pub size: usize,
|
||||
+ pub storage_type: StorageType,
|
||||
}
|
||||
|
||||
/// Main object for the kernel. Each board will need to create one.
|
||||
@@ -1,13 +0,0 @@
|
||||
diff --git a/arch/cortex-m/src/syscall.rs b/arch/cortex-m/src/syscall.rs
|
||||
index c78b1c9fb..2769d0138 100644
|
||||
--- a/arch/cortex-m/src/syscall.rs
|
||||
+++ b/arch/cortex-m/src/syscall.rs
|
||||
@@ -65,7 +65,7 @@ impl kernel::syscall::UserspaceKernelBoundary for SysCall {
|
||||
//
|
||||
// The 1.x Tock kernel allocates at least 3 kB to processes, and we need
|
||||
// to ensure that happens as userspace may expect it.
|
||||
- 3 * 1024
|
||||
+ 16 * 1024
|
||||
|
||||
// TOCK 2.0
|
||||
//
|
||||
@@ -25,21 +25,20 @@ index e6cf3ff56..7b3ea5a59 100644
|
||||
if uicr
|
||||
.get_psel0_reset_pin()
|
||||
diff --git a/chips/nrf52/src/uicr.rs b/chips/nrf52/src/uicr.rs
|
||||
index 4924bbd44..788533210 100644
|
||||
index 3b51717d0..3086394aa 100644
|
||||
--- a/chips/nrf52/src/uicr.rs
|
||||
+++ b/chips/nrf52/src/uicr.rs
|
||||
@@ -1,38 +1,44 @@
|
||||
@@ -1,39 +1,43 @@
|
||||
//! User information configuration registers
|
||||
-//!
|
||||
-//! Minimal implementation to support activation of the reset button on
|
||||
-//! nRF52-DK.
|
||||
|
||||
use enum_primitive::cast::FromPrimitive;
|
||||
-use kernel::common::registers::{register_bitfields, ReadWrite};
|
||||
+use kernel::common::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::common::StaticRef;
|
||||
+use kernel::hil;
|
||||
+use kernel::ReturnCode;
|
||||
use kernel::utilities::registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
-use kernel::utilities::registers::{register_bitfields, ReadWrite};
|
||||
+use kernel::utilities::registers::{register_bitfields, register_structs, ReadWrite};
|
||||
use kernel::utilities::StaticRef;
|
||||
|
||||
use crate::gpio::Pin;
|
||||
|
||||
@@ -70,40 +69,41 @@ index 4924bbd44..788533210 100644
|
||||
+ unsafe { StaticRef::new(0x10001000 as *const UicrRegisters) };
|
||||
+
|
||||
+register_structs! {
|
||||
+ UicrRegisters {
|
||||
+ (0x000 => _reserved1),
|
||||
+ /// Reserved for Nordic firmware design
|
||||
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
|
||||
+ (0x048 => _reserved2),
|
||||
+ /// Reserved for Nordic hardware design
|
||||
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
|
||||
+ /// Reserved for customer
|
||||
+ (0x080 => customer: [ReadWrite<u32>; 32]),
|
||||
+ (0x100 => _reserved3),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Access Port protection
|
||||
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
|
||||
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
+ /// - Address: 0x20c - 0x210
|
||||
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
|
||||
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
|
||||
+ (0x214 => _reserved4),
|
||||
+ /// External circuitry to be supplied from VDD pin.
|
||||
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
|
||||
+ /// GPIO reference voltage
|
||||
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
|
||||
+ (0x308 => @END),
|
||||
+ }
|
||||
+ UicrRegisters {
|
||||
+ (0x000 => _reserved1),
|
||||
+ /// Reserved for Nordic firmware design
|
||||
+ (0x014 => nrffw: [ReadWrite<u32>; 13]),
|
||||
+ (0x048 => _reserved2),
|
||||
+ /// Reserved for Nordic hardware design
|
||||
+ (0x050 => nrfhw: [ReadWrite<u32>; 12]),
|
||||
+ /// Reserved for customer
|
||||
+ (0x080 => customer: [ReadWrite<u32>; 32]),
|
||||
+ (0x100 => _reserved3),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x200 => pselreset0: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Mapping of the nRESET function (see POWER chapter for details)
|
||||
+ (0x204 => pselreset1: ReadWrite<u32, Pselreset::Register>),
|
||||
+ /// Access Port protection
|
||||
+ (0x208 => approtect: ReadWrite<u32, ApProtect::Register>),
|
||||
+ /// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
+ /// - Address: 0x20c - 0x210
|
||||
+ (0x20c => nfcpins: ReadWrite<u32, NfcPins::Register>),
|
||||
+ (0x210 => debugctrl: ReadWrite<u32, DebugControl::Register>),
|
||||
+ (0x214 => _reserved4),
|
||||
+ /// External circuitry to be supplied from VDD pin.
|
||||
+ (0x300 => extsupply: ReadWrite<u32, ExtSupply::Register>),
|
||||
+ /// GPIO reference voltage
|
||||
+ (0x304 => regout0: ReadWrite<u32, RegOut::Register>),
|
||||
+ (0x308 => @END),
|
||||
+ }
|
||||
}
|
||||
|
||||
register_bitfields! [u32,
|
||||
@@ -58,6 +64,21 @@ register_bitfields! [u32,
|
||||
@@ -59,6 +63,23 @@ register_bitfields! [u32,
|
||||
DISABLED = 0xff
|
||||
]
|
||||
],
|
||||
+
|
||||
+ /// Processor debug control
|
||||
+ DebugControl [
|
||||
+ CPUNIDEN OFFSET(0) NUMBITS(8) [
|
||||
@@ -119,11 +119,12 @@ index 4924bbd44..788533210 100644
|
||||
+ DISABLED = 0x00
|
||||
+ ]
|
||||
+ ],
|
||||
+
|
||||
/// Setting of pins dedicated to NFC functionality: NFC antenna or GPIO
|
||||
NfcPins [
|
||||
/// Setting pins dedicated to NFC functionality
|
||||
@@ -172,6 +193,18 @@ impl Uicr {
|
||||
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
|
||||
@@ -169,6 +190,18 @@ impl Uicr {
|
||||
}
|
||||
}
|
||||
|
||||
+ pub fn get_dfu_params(&self) -> (u32, u32) {
|
||||
@@ -138,6 +139,6 @@ index 4924bbd44..788533210 100644
|
||||
+ self.registers.nrffw[1].set(dfu_settings_addr);
|
||||
+ }
|
||||
+
|
||||
pub fn is_ap_protect_enabled(&self) -> bool {
|
||||
// Here we compare to DISABLED value because any other value should enable the protection.
|
||||
!self
|
||||
pub fn is_nfc_pins_protection_enabled(&self) -> bool {
|
||||
self.registers.nfcpins.matches_all(NfcPins::PROTECT::NFC)
|
||||
}
|
||||
@@ -1,329 +0,0 @@
|
||||
diff --git a/capsules/src/usb/app.rs b/capsules/src/usb/app.rs
|
||||
new file mode 100644
|
||||
index 000000000..c2f434f12
|
||||
--- /dev/null
|
||||
+++ b/capsules/src/usb/app.rs
|
||||
@@ -0,0 +1,65 @@
|
||||
+use kernel::{AppSlice, Callback, Shared};
|
||||
+
|
||||
+#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
+pub enum Side {
|
||||
+ Transmit,
|
||||
+ Receive,
|
||||
+ TransmitOrReceive,
|
||||
+}
|
||||
+
|
||||
+impl Side {
|
||||
+ pub fn can_transmit(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Transmit | Side::TransmitOrReceive => true,
|
||||
+ Side::Receive => false,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn can_receive(&self) -> bool {
|
||||
+ match self {
|
||||
+ Side::Receive | Side::TransmitOrReceive => true,
|
||||
+ Side::Transmit => false,
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+#[derive(Default)]
|
||||
+pub struct App {
|
||||
+ // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
+ // This field tracks this status.
|
||||
+ pub connected: bool,
|
||||
+ // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
+ // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
+ // the side to None.
|
||||
+ pub side: Option<Side>,
|
||||
+ pub callback: Option<Callback>,
|
||||
+ pub buffer: Option<AppSlice<Shared, u8>>,
|
||||
+ // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
+ pub waiting: bool,
|
||||
+}
|
||||
+
|
||||
+impl App {
|
||||
+ pub fn can_receive_packet(&self) -> bool {
|
||||
+ self.waiting && self.side.map_or(false, |side| side.can_receive()) && self.buffer.is_some()
|
||||
+ }
|
||||
+
|
||||
+ pub fn check_side(&mut self) {
|
||||
+ if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
+ self.side = None;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn set_side(&mut self, side: Side) -> bool {
|
||||
+ match self.side {
|
||||
+ None => {
|
||||
+ self.side = Some(side);
|
||||
+ true
|
||||
+ }
|
||||
+ Some(app_side) => side == app_side,
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ pub fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
+ self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
+ }
|
||||
+}
|
||||
diff --git a/capsules/src/usb/mod.rs b/capsules/src/usb/mod.rs
|
||||
index 3f3a4f646..cb5e0af97 100644
|
||||
--- a/capsules/src/usb/mod.rs
|
||||
+++ b/capsules/src/usb/mod.rs
|
||||
@@ -1,3 +1,4 @@
|
||||
+pub mod app;
|
||||
pub mod cdc;
|
||||
pub mod descriptors;
|
||||
pub mod usb_ctap;
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
index da3d16d85..e8f1a87a4 100644
|
||||
--- a/capsules/src/usb/usb_ctap.rs
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -1,7 +1,7 @@
|
||||
+use super::app::{App, Side};
|
||||
use super::usbc_ctap_hid::ClientCtapHID;
|
||||
-use kernel::hil;
|
||||
use kernel::hil::usb::Client;
|
||||
-use kernel::{AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
+use kernel::{hil, AppId, AppSlice, Callback, Driver, Grant, ReturnCode, Shared};
|
||||
|
||||
/// Syscall number
|
||||
use crate::driver;
|
||||
@@ -25,73 +25,15 @@ pub const CTAP_SUBSCRIBE_TRANSMIT_OR_RECEIVE: usize = 3;
|
||||
pub const CTAP_CALLBACK_TRANSMITED: usize = 1;
|
||||
pub const CTAP_CALLBACK_RECEIVED: usize = 2;
|
||||
|
||||
-#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
-enum Side {
|
||||
- Transmit,
|
||||
- Receive,
|
||||
- TransmitOrReceive,
|
||||
-}
|
||||
-
|
||||
-impl Side {
|
||||
- fn can_transmit(&self) -> bool {
|
||||
- match self {
|
||||
- Side::Transmit | Side::TransmitOrReceive => true,
|
||||
- Side::Receive => false,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn can_receive(&self) -> bool {
|
||||
- match self {
|
||||
- Side::Receive | Side::TransmitOrReceive => true,
|
||||
- Side::Transmit => false,
|
||||
- }
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-#[derive(Default)]
|
||||
-pub struct App {
|
||||
- // Only one app can be connected to this driver, to avoid needing to route packets among apps.
|
||||
- // This field tracks this status.
|
||||
- connected: bool,
|
||||
- // Currently enabled transaction side. Subscribing to a callback or allowing a buffer
|
||||
- // automatically sets the corresponding side. Clearing both the callback and the buffer resets
|
||||
- // the side to None.
|
||||
- side: Option<Side>,
|
||||
- callback: Option<Callback>,
|
||||
- buffer: Option<AppSlice<Shared, u8>>,
|
||||
- // Whether the app is waiting for the kernel signaling a packet transfer.
|
||||
- waiting: bool,
|
||||
-}
|
||||
-
|
||||
-impl App {
|
||||
- fn check_side(&mut self) {
|
||||
- if self.callback.is_none() && self.buffer.is_none() && !self.waiting {
|
||||
- self.side = None;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn set_side(&mut self, side: Side) -> bool {
|
||||
- match self.side {
|
||||
- None => {
|
||||
- self.side = Some(side);
|
||||
- true
|
||||
- }
|
||||
- Some(app_side) => side == app_side,
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- fn is_ready_for_command(&self, side: Side) -> bool {
|
||||
- self.buffer.is_some() && self.callback.is_some() && self.side == Some(side)
|
||||
- }
|
||||
-}
|
||||
-
|
||||
pub trait CtapUsbClient {
|
||||
// Whether this client is ready to receive a packet. This must be checked before calling
|
||||
- // packet_received().
|
||||
- fn can_receive_packet(&self) -> bool;
|
||||
+ // packet_received(). If App is not supplied, it will be found from the implemntation's
|
||||
+ // members.
|
||||
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
|
||||
|
||||
// Signal to the client that a packet has been received.
|
||||
- fn packet_received(&self, packet: &[u8; 64]);
|
||||
+ // If App is not supplied, it will be found from the implemntation's members.
|
||||
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
|
||||
|
||||
// Signal to the client that a packet has been transmitted.
|
||||
fn packet_transmitted(&self);
|
||||
@@ -106,38 +48,49 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
pub fn new(usb_client: &'a ClientCtapHID<'a, 'b, C>, apps: Grant<App>) -> Self {
|
||||
CtapUsbSyscallDriver { usb_client, apps }
|
||||
}
|
||||
+
|
||||
+ fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
|
||||
+ if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
|
||||
+ if let Some(buf) = &mut app.buffer {
|
||||
+ // Copy the packet to the app's allowed buffer.
|
||||
+ buf.as_mut().copy_from_slice(packet);
|
||||
+ app.waiting = false;
|
||||
+ // Signal to the app that a packet is ready.
|
||||
+ app.callback
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
|
||||
impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
- fn can_receive_packet(&self) -> bool {
|
||||
+ fn can_receive_packet(&self, app: &Option<&mut App>) -> bool {
|
||||
let mut result = false;
|
||||
- for app in self.apps.iter() {
|
||||
- app.enter(|app, _| {
|
||||
- if app.connected {
|
||||
- result = app.waiting
|
||||
- && app.side.map_or(false, |side| side.can_receive())
|
||||
- && app.buffer.is_some();
|
||||
+ match app {
|
||||
+ None => {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|a, _| {
|
||||
+ if a.connected {
|
||||
+ result = a.can_receive_packet();
|
||||
+ }
|
||||
+ })
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+ Some(a) => result = a.can_receive_packet(),
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
- fn packet_received(&self, packet: &[u8; 64]) {
|
||||
- for app in self.apps.iter() {
|
||||
- app.enter(|app, _| {
|
||||
- if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive())
|
||||
- {
|
||||
- if let Some(buf) = &mut app.buffer {
|
||||
- // Copy the packet to the app's allowed buffer.
|
||||
- buf.as_mut().copy_from_slice(packet);
|
||||
- app.waiting = false;
|
||||
- // Signal to the app that a packet is ready.
|
||||
- app.callback
|
||||
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
- }
|
||||
+ fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
|
||||
+ match app {
|
||||
+ None => {
|
||||
+ for app in self.apps.iter() {
|
||||
+ app.enter(|a, _| {
|
||||
+ self.app_packet_received(packet, a);
|
||||
+ })
|
||||
}
|
||||
- });
|
||||
+ }
|
||||
+ Some(a) => self.app_packet_received(packet, a),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +235,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
ReturnCode::EALREADY
|
||||
} else {
|
||||
app.waiting = true;
|
||||
- self.usb_client.receive_packet();
|
||||
+ self.usb_client.receive_packet(app);
|
||||
ReturnCode::SUCCESS
|
||||
}
|
||||
} else {
|
||||
@@ -303,7 +256,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
} else {
|
||||
// Indicates to the driver that we can receive any pending packet.
|
||||
app.waiting = true;
|
||||
- self.usb_client.receive_packet();
|
||||
+ self.usb_client.receive_packet(app);
|
||||
|
||||
if !app.waiting {
|
||||
// The call to receive_packet() collected a pending packet.
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index abf224f97..d47e5f644 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -11,6 +11,7 @@ use super::descriptors::HIDSubordinateDescriptor;
|
||||
use super::descriptors::InterfaceDescriptor;
|
||||
use super::descriptors::ReportDescriptor;
|
||||
use super::descriptors::TransferDirection;
|
||||
+use super::app::App;
|
||||
use super::usb_ctap::CtapUsbClient;
|
||||
use super::usbc_client_ctrl::ClientCtrl;
|
||||
use core::cell::Cell;
|
||||
@@ -257,7 +258,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn receive_packet(&'a self) -> bool {
|
||||
+ pub fn receive_packet(&'a self, app: &mut App) -> bool {
|
||||
if self.pending_out.get() {
|
||||
// The previous packet has not yet been received, reject the new one.
|
||||
false
|
||||
@@ -267,7 +268,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
// packet arrives.
|
||||
if self.delayed_out.take() {
|
||||
- if self.send_packet_to_client() {
|
||||
+ if self.send_packet_to_client(Some(app)) {
|
||||
// If that succeeds, alert the controller that we can now
|
||||
// receive data on the Interrupt OUT endpoint.
|
||||
self.controller().endpoint_resume_out(1);
|
||||
@@ -280,7 +281,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Send an OUT packet available in the controller back to the client.
|
||||
// This returns false if the client is not ready to receive a packet, and true if the client
|
||||
// successfully accepted the packet.
|
||||
- fn send_packet_to_client(&'a self) -> bool {
|
||||
+ fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
|
||||
// Copy the packet into a buffer to send to the client.
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
@@ -292,7 +293,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// Notify the client
|
||||
if self
|
||||
.client
|
||||
- .map_or(false, |client| client.can_receive_packet())
|
||||
+ .map_or(false, |client| client.can_receive_packet(&app))
|
||||
{
|
||||
assert!(self.pending_out.take());
|
||||
|
||||
@@ -301,7 +302,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
// should be re-transmitted or not.
|
||||
self.cancel_in_transaction();
|
||||
|
||||
- self.client.map(|client| client.packet_received(&buf));
|
||||
+ self.client.map(|client| client.packet_received(&buf, app));
|
||||
true
|
||||
} else {
|
||||
// Cannot receive now, indicate a delay to the controller.
|
||||
@@ -421,7 +422,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Cannot process this packet
|
||||
hil::usb::OutResult::Error
|
||||
} else {
|
||||
- if self.send_packet_to_client() {
|
||||
+ if self.send_packet_to_client(None) {
|
||||
hil::usb::OutResult::Ok
|
||||
} else {
|
||||
hil::usb::OutResult::Delay
|
||||
@@ -1,516 +0,0 @@
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
index e8f1a87a4..2c91c0968 100644
|
||||
--- a/capsules/src/usb/usb_ctap.rs
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -32,8 +32,7 @@ pub trait CtapUsbClient {
|
||||
fn can_receive_packet(&self, app: &Option<&mut App>) -> bool;
|
||||
|
||||
// Signal to the client that a packet has been received.
|
||||
- // If App is not supplied, it will be found from the implemntation's members.
|
||||
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>);
|
||||
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>);
|
||||
|
||||
// Signal to the client that a packet has been transmitted.
|
||||
fn packet_transmitted(&self);
|
||||
@@ -49,7 +48,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
CtapUsbSyscallDriver { usb_client, apps }
|
||||
}
|
||||
|
||||
- fn app_packet_received(&self, packet: &[u8; 64], app: &mut App) {
|
||||
+ fn app_packet_received(&self, packet: &[u8; 64], endpoint: usize, app: &mut App) {
|
||||
if app.connected && app.waiting && app.side.map_or(false, |side| side.can_receive()) {
|
||||
if let Some(buf) = &mut app.buffer {
|
||||
// Copy the packet to the app's allowed buffer.
|
||||
@@ -57,7 +56,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbSyscallDriver<'a, 'b, C> {
|
||||
app.waiting = false;
|
||||
// Signal to the app that a packet is ready.
|
||||
app.callback
|
||||
- .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, 0, 0));
|
||||
+ .map(|mut cb| cb.schedule(CTAP_CALLBACK_RECEIVED, endpoint, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,16 +80,16 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> CtapUsbClient for CtapUsbSyscallDri
|
||||
result
|
||||
}
|
||||
|
||||
- fn packet_received(&self, packet: &[u8; 64], app: Option<&mut App>) {
|
||||
+ fn packet_received(&self, packet: &[u8; 64], endpoint: usize, app: Option<&mut App>) {
|
||||
match app {
|
||||
None => {
|
||||
for app in self.apps.iter() {
|
||||
app.enter(|a, _| {
|
||||
- self.app_packet_received(packet, a);
|
||||
+ self.app_packet_received(packet, endpoint, a);
|
||||
})
|
||||
}
|
||||
}
|
||||
- Some(a) => self.app_packet_received(packet, a),
|
||||
+ Some(a) => self.app_packet_received(packet, endpoint, a),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,7 +172,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
.unwrap_or_else(|err| err.into())
|
||||
}
|
||||
|
||||
- fn command(&self, cmd_num: usize, _arg1: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
+ fn command(&self, cmd_num: usize, endpoint: usize, _arg2: usize, appid: AppId) -> ReturnCode {
|
||||
match cmd_num {
|
||||
CTAP_CMD_CHECK => ReturnCode::SUCCESS,
|
||||
CTAP_CMD_CONNECT => {
|
||||
@@ -209,14 +208,14 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
if app.is_ready_for_command(Side::Transmit) {
|
||||
if app.waiting {
|
||||
ReturnCode::EALREADY
|
||||
- } else if self
|
||||
- .usb_client
|
||||
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
- {
|
||||
- app.waiting = true;
|
||||
- ReturnCode::SUCCESS
|
||||
} else {
|
||||
- ReturnCode::EBUSY
|
||||
+ let r = self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint);
|
||||
+ if r == ReturnCode::SUCCESS {
|
||||
+ app.waiting = true;
|
||||
+ }
|
||||
+ r
|
||||
}
|
||||
} else {
|
||||
ReturnCode::EINVAL
|
||||
@@ -263,14 +262,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
ReturnCode::SUCCESS
|
||||
} else {
|
||||
// Indicates to the driver that we have a packet to send.
|
||||
- if self
|
||||
- .usb_client
|
||||
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref())
|
||||
- {
|
||||
- ReturnCode::SUCCESS
|
||||
- } else {
|
||||
- ReturnCode::EBUSY
|
||||
- }
|
||||
+ self.usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -289,7 +282,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
// FIXME: if cancellation failed, the app should still wait. But that
|
||||
// doesn't work yet.
|
||||
app.waiting = false;
|
||||
- if self.usb_client.cancel_transaction() {
|
||||
+ if self.usb_client.cancel_transaction(endpoint) {
|
||||
ReturnCode::SUCCESS
|
||||
} else {
|
||||
// Cannot cancel now because the transaction is already in process.
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index d47e5f644..76f6af73b 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -18,13 +18,27 @@ use core::cell::Cell;
|
||||
use kernel::common::cells::OptionalCell;
|
||||
use kernel::debug;
|
||||
use kernel::hil;
|
||||
+use kernel::ReturnCode;
|
||||
use kernel::hil::usb::TransferType;
|
||||
|
||||
static LANGUAGES: &'static [u16; 1] = &[
|
||||
0x0409, // English (United States)
|
||||
];
|
||||
|
||||
+#[cfg(not(feature = "vendor_hid"))]
|
||||
+const NUM_ENDPOINTS: usize = 1;
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+const NUM_ENDPOINTS: usize = 2;
|
||||
+
|
||||
const ENDPOINT_NUM: usize = 1;
|
||||
+#[cfg(feature = "vendor_hid")]
|
||||
+const VENDOR_ENDPOINT_NUM: usize = ENDPOINT_NUM + 1;
|
||||
+
|
||||
+static ENDPOINTS: &'static [usize] = &[
|
||||
+ ENDPOINT_NUM,
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ VENDOR_ENDPOINT_NUM
|
||||
+];
|
||||
|
||||
static CTAP_REPORT_DESCRIPTOR: &'static [u8] = &[
|
||||
0x06, 0xD0, 0xF1, // HID_UsagePage ( FIDO_USAGE_PAGE ),
|
||||
@@ -98,21 +112,42 @@ static VENDOR_HID: HIDDescriptor<'static> = HIDDescriptor {
|
||||
sub_descriptors: VENDOR_HID_SUB_DESCRIPTORS,
|
||||
};
|
||||
|
||||
-pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
- client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
-
|
||||
- // 64-byte buffers for the endpoint
|
||||
+// The state of each endpoint.
|
||||
+struct EndpointState {
|
||||
+ endpoint: usize,
|
||||
in_buffer: Buffer64,
|
||||
out_buffer: Buffer64,
|
||||
|
||||
- // Interaction with the client
|
||||
- client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
tx_packet: OptionalCell<[u8; 64]>,
|
||||
pending_in: Cell<bool>,
|
||||
pending_out: Cell<bool>,
|
||||
+ // Is there a delayed packet?
|
||||
delayed_out: Cell<bool>,
|
||||
}
|
||||
|
||||
+impl EndpointState {
|
||||
+ pub fn new(endpoint:usize) -> Self {
|
||||
+ EndpointState{
|
||||
+ endpoint: endpoint,
|
||||
+ in_buffer: Buffer64::default(),
|
||||
+ out_buffer: Buffer64::default(),
|
||||
+ tx_packet: OptionalCell::empty(),
|
||||
+ pending_in: Cell::new(false),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ delayed_out: Cell::new(false),
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
+ client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
+
|
||||
+ endpoints: [EndpointState; NUM_ENDPOINTS],
|
||||
+
|
||||
+ // Interaction with the client
|
||||
+ client: OptionalCell<&'b dyn CtapUsbClient>,
|
||||
+}
|
||||
+
|
||||
impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
pub fn new(
|
||||
controller: &'a C,
|
||||
@@ -168,7 +203,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
&[
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
- ENDPOINT_NUM + 1,
|
||||
+ VENDOR_ENDPOINT_NUM,
|
||||
TransferDirection::HostToDevice,
|
||||
),
|
||||
transfer_type: TransferType::Interrupt,
|
||||
@@ -177,7 +212,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
},
|
||||
EndpointDescriptor {
|
||||
endpoint_address: EndpointAddress::new_const(
|
||||
- ENDPOINT_NUM + 1,
|
||||
+ VENDOR_ENDPOINT_NUM,
|
||||
TransferDirection::DeviceToHost,
|
||||
),
|
||||
transfer_type: TransferType::Interrupt,
|
||||
@@ -229,99 +264,135 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
- in_buffer: Buffer64::default(),
|
||||
- out_buffer: Buffer64::default(),
|
||||
+ endpoints: [
|
||||
+ EndpointState::new(ENDPOINT_NUM),
|
||||
+ #[cfg(feature = "vendor_hid")]
|
||||
+ EndpointState::new(VENDOR_ENDPOINT_NUM),
|
||||
+ ],
|
||||
client: OptionalCell::empty(),
|
||||
- tx_packet: OptionalCell::empty(),
|
||||
- pending_in: Cell::new(false),
|
||||
- pending_out: Cell::new(false),
|
||||
- delayed_out: Cell::new(false),
|
||||
}
|
||||
}
|
||||
|
||||
+ fn get_endpoint(&'a self, endpoint: usize) -> Option<&'a EndpointState> {
|
||||
+ for (i, ep) in ENDPOINTS.iter().enumerate() {
|
||||
+ if endpoint == *ep {
|
||||
+ return Some(&self.endpoints[i]);
|
||||
+ }
|
||||
+ }
|
||||
+ None
|
||||
+ }
|
||||
+
|
||||
pub fn set_client(&'a self, client: &'b dyn CtapUsbClient) {
|
||||
self.client.set(client);
|
||||
}
|
||||
|
||||
- pub fn transmit_packet(&'a self, packet: &[u8]) -> bool {
|
||||
- if self.pending_in.get() {
|
||||
- // The previous packet has not yet been transmitted, reject the new one.
|
||||
- false
|
||||
- } else {
|
||||
- self.pending_in.set(true);
|
||||
+ pub fn transmit_packet(&'a self, packet: &[u8], endpoint: usize) -> ReturnCode {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if s.pending_in.get() {
|
||||
+ // The previous packet has not yet been transmitted, reject the new one.
|
||||
+ return ReturnCode::EBUSY;
|
||||
+ }
|
||||
+ s.pending_in.set(true);
|
||||
let mut buf: [u8; 64] = [0; 64];
|
||||
buf.copy_from_slice(packet);
|
||||
- self.tx_packet.set(buf);
|
||||
+ s.tx_packet.set(buf);
|
||||
// Alert the controller that we now have data to send on the Interrupt IN endpoint.
|
||||
- self.controller().endpoint_resume_in(1);
|
||||
- true
|
||||
+ self.controller().endpoint_resume_in(endpoint);
|
||||
+ ReturnCode::SUCCESS
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ ReturnCode::EINVAL
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn receive_packet(&'a self, app: &mut App) -> bool {
|
||||
- if self.pending_out.get() {
|
||||
- // The previous packet has not yet been received, reject the new one.
|
||||
- false
|
||||
- } else {
|
||||
- self.pending_out.set(true);
|
||||
- // In case we reported Delay before, send the pending packet back to the client.
|
||||
- // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
- // packet arrives.
|
||||
- if self.delayed_out.take() {
|
||||
- if self.send_packet_to_client(Some(app)) {
|
||||
- // If that succeeds, alert the controller that we can now
|
||||
- // receive data on the Interrupt OUT endpoint.
|
||||
- self.controller().endpoint_resume_out(1);
|
||||
+ pub fn receive_packet(&'a self, app: &mut App) {
|
||||
+ for (_, s) in self.endpoints.iter().enumerate() {
|
||||
+ if s.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ continue;
|
||||
+ } else {
|
||||
+ s.pending_out.set(true);
|
||||
+ // In case we reported Delay before, send the pending packet back to the client.
|
||||
+ // Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
+ // packet arrives.
|
||||
+ if s.delayed_out.take() {
|
||||
+ if self.send_packet_to_client(s.endpoint, Some(app)) {
|
||||
+ // If that succeeds, alert the controller that we can now
|
||||
+ // receive data on the Interrupt OUT endpoint.
|
||||
+ self.controller().endpoint_resume_out(s.endpoint);
|
||||
+ }
|
||||
}
|
||||
}
|
||||
- true
|
||||
}
|
||||
}
|
||||
|
||||
// Send an OUT packet available in the controller back to the client.
|
||||
// This returns false if the client is not ready to receive a packet, and true if the client
|
||||
// successfully accepted the packet.
|
||||
- fn send_packet_to_client(&'a self, app: Option<&mut App>) -> bool {
|
||||
- // Copy the packet into a buffer to send to the client.
|
||||
- let mut buf: [u8; 64] = [0; 64];
|
||||
- for (i, x) in self.out_buffer.buf.iter().enumerate() {
|
||||
- buf[i] = x.get();
|
||||
- }
|
||||
-
|
||||
- assert!(!self.delayed_out.get());
|
||||
-
|
||||
- // Notify the client
|
||||
- if self
|
||||
- .client
|
||||
- .map_or(false, |client| client.can_receive_packet(&app))
|
||||
- {
|
||||
- assert!(self.pending_out.take());
|
||||
-
|
||||
- // Clear any pending packet on the transmitting side.
|
||||
- // It's up to the client to handle the received packet and decide if this packet
|
||||
- // should be re-transmitted or not.
|
||||
- self.cancel_in_transaction();
|
||||
+ fn send_packet_to_client(&'a self, endpoint: usize, app: Option<&mut App>) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ // Copy the packet into a buffer to send to the client.
|
||||
+ let mut buf: [u8; 64] = [0; 64];
|
||||
+ for (i, x) in s.out_buffer.buf.iter().enumerate() {
|
||||
+ buf[i] = x.get();
|
||||
+ }
|
||||
|
||||
- self.client.map(|client| client.packet_received(&buf, app));
|
||||
- true
|
||||
+ assert!(!s.delayed_out.get());
|
||||
+
|
||||
+ // Notify the client
|
||||
+ if self
|
||||
+ .client
|
||||
+ .map_or(false, |client| client.can_receive_packet(&app))
|
||||
+ {
|
||||
+ assert!(s.pending_out.take());
|
||||
+
|
||||
+ // Clear any pending packet on the transmitting side.
|
||||
+ // It's up to the client to handle the received packet and decide if this packet
|
||||
+ // should be re-transmitted or not.
|
||||
+ self.cancel_in_transaction(endpoint);
|
||||
+
|
||||
+ self.client.map(|client| client.packet_received(&buf, endpoint, app));
|
||||
+ true
|
||||
+ } else {
|
||||
+ // Cannot receive now, indicate a delay to the controller.
|
||||
+ s.delayed_out.set(true);
|
||||
+ false
|
||||
+ }
|
||||
} else {
|
||||
- // Cannot receive now, indicate a delay to the controller.
|
||||
- self.delayed_out.set(true);
|
||||
+ // Unsupported endpoint
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
- pub fn cancel_transaction(&'a self) -> bool {
|
||||
- self.cancel_in_transaction() | self.cancel_out_transaction()
|
||||
+ // Cancel transaction(s) in process. |endpoint| of 0 indicates all endpoints.
|
||||
+ pub fn cancel_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if endpoint > 0 {
|
||||
+ return self.cancel_in_transaction(endpoint) | self.cancel_out_transaction(endpoint);
|
||||
+ }
|
||||
+ let mut r = false;
|
||||
+ for (_, s) in self.endpoints.iter().enumerate() {
|
||||
+ r |= self.cancel_in_transaction(s.endpoint) | self.cancel_out_transaction(s.endpoint);
|
||||
+ }
|
||||
+ r
|
||||
}
|
||||
|
||||
- fn cancel_in_transaction(&'a self) -> bool {
|
||||
- self.tx_packet.take();
|
||||
- self.pending_in.take()
|
||||
+ fn cancel_in_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ s.tx_packet.take();
|
||||
+ s.pending_in.take()
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ false
|
||||
+ }
|
||||
}
|
||||
|
||||
- fn cancel_out_transaction(&'a self) -> bool {
|
||||
- self.pending_out.take()
|
||||
+ fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ s.pending_out.take()
|
||||
+ } else {
|
||||
+ // Unsupported endpoint
|
||||
+ false
|
||||
+ }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -335,13 +406,15 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Set up the default control endpoint
|
||||
self.client_ctrl.enable();
|
||||
|
||||
- // Set up the interrupt in-out endpoint
|
||||
- self.controller()
|
||||
- .endpoint_set_in_buffer(1, &self.in_buffer.buf);
|
||||
- self.controller()
|
||||
- .endpoint_set_out_buffer(1, &self.out_buffer.buf);
|
||||
- self.controller()
|
||||
- .endpoint_in_out_enable(TransferType::Interrupt, 1);
|
||||
+ // Set up the interrupt in-out endpoint(s).
|
||||
+ for (i, endpoint) in ENDPOINTS.iter().enumerate() {
|
||||
+ self.controller()
|
||||
+ .endpoint_set_in_buffer(*endpoint, &self.endpoints[i].in_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_set_out_buffer(*endpoint, &self.endpoints[i].out_buffer.buf);
|
||||
+ self.controller()
|
||||
+ .endpoint_in_out_enable(TransferType::Interrupt, *endpoint);
|
||||
+ }
|
||||
}
|
||||
|
||||
fn attach(&'a self) {
|
||||
@@ -384,20 +457,20 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
match transfer_type {
|
||||
TransferType::Bulk => hil::usb::InResult::Error,
|
||||
TransferType::Interrupt => {
|
||||
- if endpoint != 1 {
|
||||
- return hil::usb::InResult::Error;
|
||||
- }
|
||||
-
|
||||
- if let Some(packet) = self.tx_packet.take() {
|
||||
- let buf = &self.in_buffer.buf;
|
||||
- for i in 0..64 {
|
||||
- buf[i].set(packet[i]);
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if let Some(packet) = s.tx_packet.take() {
|
||||
+ let buf = &s.in_buffer.buf;
|
||||
+ for i in 0..64 {
|
||||
+ buf[i].set(packet[i]);
|
||||
+ }
|
||||
+ hil::usb::InResult::Packet(64)
|
||||
+ } else {
|
||||
+ // Nothing to send
|
||||
+ hil::usb::InResult::Delay
|
||||
}
|
||||
-
|
||||
- hil::usb::InResult::Packet(64)
|
||||
} else {
|
||||
- // Nothing to send
|
||||
- hil::usb::InResult::Delay
|
||||
+ // Unsupported endpoint
|
||||
+ return hil::usb::InResult::Error
|
||||
}
|
||||
}
|
||||
TransferType::Control | TransferType::Isochronous => unreachable!(),
|
||||
@@ -414,7 +487,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
match transfer_type {
|
||||
TransferType::Bulk => hil::usb::OutResult::Error,
|
||||
TransferType::Interrupt => {
|
||||
- if endpoint != 1 {
|
||||
+ if endpoint == 0 || endpoint > NUM_ENDPOINTS {
|
||||
return hil::usb::OutResult::Error;
|
||||
}
|
||||
|
||||
@@ -422,7 +495,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
// Cannot process this packet
|
||||
hil::usb::OutResult::Error
|
||||
} else {
|
||||
- if self.send_packet_to_client(None) {
|
||||
+ if self.send_packet_to_client(endpoint, None) {
|
||||
hil::usb::OutResult::Ok
|
||||
} else {
|
||||
hil::usb::OutResult::Delay
|
||||
@@ -434,21 +507,21 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> hil::usb::Client<'a> for ClientCtap
|
||||
}
|
||||
|
||||
fn packet_transmitted(&'a self, endpoint: usize) {
|
||||
- if endpoint != 1 {
|
||||
- panic!("Unexpected transmission on ep {}", endpoint);
|
||||
- }
|
||||
-
|
||||
- if self.tx_packet.is_some() {
|
||||
- panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
- }
|
||||
- self.pending_in.set(false);
|
||||
+ if let Some(s) = self.get_endpoint(endpoint) {
|
||||
+ if s.tx_packet.is_some() {
|
||||
+ panic!("Unexpected tx_packet while a packet was being transmitted.");
|
||||
+ }
|
||||
+ s.pending_in.set(false);
|
||||
|
||||
- // Clear any pending packet on the receiving side.
|
||||
- // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
- // receive another packet.
|
||||
- self.cancel_out_transaction();
|
||||
+ // Clear any pending packet on the receiving side.
|
||||
+ // It's up to the client to handle the transmitted packet and decide if they want to
|
||||
+ // receive another packet.
|
||||
+ self.cancel_out_transaction(endpoint);
|
||||
|
||||
- // Notify the client
|
||||
- self.client.map(|client| client.packet_transmitted());
|
||||
+ // Notify the client
|
||||
+ self.client.map(|client| client.packet_transmitted());
|
||||
+ } else {
|
||||
+ panic!("Unexpected transmission on ep {}", endpoint);
|
||||
+ }
|
||||
}
|
||||
-}
|
||||
+ }
|
||||
@@ -1,94 +0,0 @@
|
||||
diff --git a/capsules/src/usb/usbc_ctap_hid.rs b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
index 16b80cb10..949388b70 100644
|
||||
--- a/capsules/src/usb/usbc_ctap_hid.rs
|
||||
+++ b/capsules/src/usb/usbc_ctap_hid.rs
|
||||
@@ -120,7 +120,6 @@ struct EndpointState {
|
||||
|
||||
tx_packet: OptionalCell<[u8; 64]>,
|
||||
pending_in: Cell<bool>,
|
||||
- pending_out: Cell<bool>,
|
||||
// Is there a delayed packet?
|
||||
delayed_out: Cell<bool>,
|
||||
}
|
||||
@@ -133,7 +132,6 @@ impl EndpointState {
|
||||
out_buffer: Buffer64::default(),
|
||||
tx_packet: OptionalCell::empty(),
|
||||
pending_in: Cell::new(false),
|
||||
- pending_out: Cell::new(false),
|
||||
delayed_out: Cell::new(false),
|
||||
}
|
||||
}
|
||||
@@ -142,6 +140,10 @@ impl EndpointState {
|
||||
pub struct ClientCtapHID<'a, 'b, C: 'a> {
|
||||
client_ctrl: ClientCtrl<'a, 'static, C>,
|
||||
|
||||
+ // Is there a pending OUT transaction happening?
|
||||
+ pending_out: Cell<bool>,
|
||||
+ next_endpoint_index: Cell<usize>,
|
||||
+
|
||||
endpoints: [EndpointState; NUM_ENDPOINTS],
|
||||
|
||||
// Interaction with the client
|
||||
@@ -264,6 +266,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
LANGUAGES,
|
||||
strings,
|
||||
),
|
||||
+ pending_out: Cell::new(false),
|
||||
+ next_endpoint_index: Cell::new(0),
|
||||
endpoints: [
|
||||
EndpointState::new(ENDPOINT_NUM),
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
@@ -306,12 +310,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
|
||||
pub fn receive_packet(&'a self, app: &mut App) {
|
||||
- for (_, s) in self.endpoints.iter().enumerate() {
|
||||
- if s.pending_out.get() {
|
||||
- // The previous packet has not yet been received, reject the new one.
|
||||
- continue;
|
||||
- } else {
|
||||
- s.pending_out.set(true);
|
||||
+ if self.pending_out.get() {
|
||||
+ // The previous packet has not yet been received, reject the new one.
|
||||
+ } else {
|
||||
+ self.pending_out.set(true);
|
||||
+ // Process the next endpoint that has a delayed packet.
|
||||
+ for i in self.next_endpoint_index.get()..self.next_endpoint_index.get() + NUM_ENDPOINTS {
|
||||
+ let s = &self.endpoints[i % NUM_ENDPOINTS];
|
||||
// In case we reported Delay before, send the pending packet back to the client.
|
||||
// Otherwise, there's nothing to do, the controller will send us a packet_out when a
|
||||
// packet arrives.
|
||||
@@ -344,7 +349,7 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
.client
|
||||
.map_or(false, |client| client.can_receive_packet(&app))
|
||||
{
|
||||
- assert!(s.pending_out.take());
|
||||
+ assert!(self.pending_out.take());
|
||||
|
||||
// Clear any pending packet on the transmitting side.
|
||||
// It's up to the client to handle the received packet and decide if this packet
|
||||
@@ -352,6 +357,13 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
self.cancel_in_transaction(endpoint);
|
||||
|
||||
self.client.map(|client| client.packet_received(&buf, endpoint, app));
|
||||
+ // Update next packet to send.
|
||||
+ for (i, ep) in self.endpoints.iter().enumerate() {
|
||||
+ if ep.endpoint == endpoint {
|
||||
+ self.next_endpoint_index.set((i + 1) % NUM_ENDPOINTS);
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
true
|
||||
} else {
|
||||
// Cannot receive now, indicate a delay to the controller.
|
||||
@@ -387,8 +399,8 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> ClientCtapHID<'a, 'b, C> {
|
||||
}
|
||||
|
||||
fn cancel_out_transaction(&'a self, endpoint: usize) -> bool {
|
||||
- if let Some(s) = self.get_endpoint(endpoint) {
|
||||
- s.pending_out.take()
|
||||
+ if let Some(_) = self.get_endpoint(endpoint) {
|
||||
+ self.pending_out.take()
|
||||
} else {
|
||||
// Unsupported endpoint
|
||||
false
|
||||
@@ -1,32 +0,0 @@
|
||||
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
|
||||
index 2c91c0968..ea8111069 100644
|
||||
--- a/capsules/src/usb/usb_ctap.rs
|
||||
+++ b/capsules/src/usb/usb_ctap.rs
|
||||
@@ -253,18 +253,19 @@ impl<'a, 'b, C: hil::usb::UsbController<'a>> Driver for CtapUsbSyscallDriver<'a,
|
||||
if app.waiting {
|
||||
ReturnCode::EALREADY
|
||||
} else {
|
||||
+ // Indicates to the driver that we have a packet to send.
|
||||
+ let r = self
|
||||
+ .usb_client
|
||||
+ .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint);
|
||||
+ if r != ReturnCode::SUCCESS {
|
||||
+ return r;
|
||||
+ }
|
||||
// Indicates to the driver that we can receive any pending packet.
|
||||
app.waiting = true;
|
||||
self.usb_client.receive_packet(app);
|
||||
|
||||
- if !app.waiting {
|
||||
- // The call to receive_packet() collected a pending packet.
|
||||
- ReturnCode::SUCCESS
|
||||
- } else {
|
||||
- // Indicates to the driver that we have a packet to send.
|
||||
- self.usb_client
|
||||
- .transmit_packet(app.buffer.as_ref().unwrap().as_ref(), endpoint)
|
||||
- }
|
||||
+ ReturnCode::SUCCESS
|
||||
+
|
||||
}
|
||||
} else {
|
||||
ReturnCode::EINVAL
|
||||
Reference in New Issue
Block a user