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:
kaczmarczyck
2023-05-05 09:55:16 +02:00
committed by GitHub
parent 645c1ba3a7
commit f25cdd6acc
78 changed files with 4079 additions and 4699 deletions

View File

@@ -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",

View File

@@ -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;

View File

@@ -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());
+ }
+}

File diff suppressed because it is too large Load Diff

View File

@@ -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,
),

View File

@@ -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;

View 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))

View File

@@ -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.

View File

@@ -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.

View File

@@ -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
//

View File

@@ -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)
}

View File

@@ -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

View File

@@ -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);
+ }
}
-}
+ }

View File

@@ -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

View File

@@ -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