Adapts libtock drivers for vendor HID (#500)

* adapts libtock drivers for vendor HID

* status with timeout
This commit is contained in:
kaczmarczyck
2022-06-16 17:56:44 +02:00
committed by GitHub
parent 7e0c0938bb
commit 1d53f3c921
4 changed files with 117 additions and 112 deletions

View File

@@ -0,0 +1,13 @@
diff --git a/capsules/src/usb/usb_ctap.rs b/capsules/src/usb/usb_ctap.rs
index e8f1a87a4..2c1ecf934 100644
--- a/capsules/src/usb/usb_ctap.rs
+++ b/capsules/src/usb/usb_ctap.rs
@@ -57,7 +57,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, 1, 0));
}
}
}

18
src/env/tock/mod.rs vendored
View File

@@ -118,27 +118,27 @@ fn send_keepalive_up_needed(
channel: Channel, channel: Channel,
timeout: Duration<isize>, timeout: Duration<isize>,
) -> Result<(), Ctap2StatusCode> { ) -> Result<(), Ctap2StatusCode> {
let (interface, cid) = match channel { let (endpoint, cid) = match channel {
Channel::MainHid(cid) => (usb_ctap_hid::UsbInterface::MainHid, cid), Channel::MainHid(cid) => (usb_ctap_hid::UsbEndpoint::MainHid, cid),
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
Channel::VendorHid(cid) => (usb_ctap_hid::UsbInterface::VendorHid, cid), Channel::VendorHid(cid) => (usb_ctap_hid::UsbEndpoint::VendorHid, cid),
}; };
let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded); let keepalive_msg = CtapHid::keepalive(cid, KeepaliveStatus::UpNeeded);
for mut pkt in keepalive_msg { for mut pkt in keepalive_msg {
let status = usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout, interface); let status =
usb_ctap_hid::send_or_recv_with_timeout(&mut pkt, timeout, endpoint).flex_unwrap();
match status { match status {
None => { usb_ctap_hid::SendOrRecvStatus::Timeout => {
debug_ctap!(env, "Sending a KEEPALIVE packet timed out"); debug_ctap!(env, "Sending a KEEPALIVE packet timed out");
// TODO: abort user presence test? // TODO: abort user presence test?
} }
Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending KEEPALIVE packet"), usb_ctap_hid::SendOrRecvStatus::Sent => {
Some(usb_ctap_hid::SendOrRecvStatus::Sent) => {
debug_ctap!(env, "Sent KEEPALIVE packet"); debug_ctap!(env, "Sent KEEPALIVE packet");
} }
Some(usb_ctap_hid::SendOrRecvStatus::Received(received_interface)) => { usb_ctap_hid::SendOrRecvStatus::Received(received_endpoint) => {
// We only parse one packet, because we only care about CANCEL. // We only parse one packet, because we only care about CANCEL.
let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt); let (received_cid, processed_packet) = CtapHid::process_single_packet(&pkt);
if received_interface != interface || received_cid != &cid { if received_endpoint != endpoint || received_cid != &cid {
debug_ctap!( debug_ctap!(
env, env,
"Received a packet on channel ID {:?} while sending a KEEPALIVE packet", "Received a packet on channel ID {:?} while sending a KEEPALIVE packet",

View File

@@ -42,7 +42,6 @@ use embedded_time::duration::Milliseconds;
use libtock_drivers::buttons::{self, ButtonState}; use libtock_drivers::buttons::{self, ButtonState};
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use libtock_drivers::console::Console; use libtock_drivers::console::Console;
#[cfg(feature = "with_ctap1")]
use libtock_drivers::result::FlexUnwrap; use libtock_drivers::result::FlexUnwrap;
use libtock_drivers::timer::Duration; use libtock_drivers::timer::Duration;
use libtock_drivers::usb_ctap_hid; use libtock_drivers::usb_ctap_hid;
@@ -89,15 +88,19 @@ fn main() {
} }
let mut pkt_request = [0; 64]; let mut pkt_request = [0; 64];
let usb_interface = let usb_endpoint =
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) { match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK)
Some(usb_ctap_hid::SendOrRecvStatus::Received(interface)) => { .flex_unwrap()
{
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Received packet", &clock); print_packet_notice("Received packet", &clock);
Some(interface) Some(endpoint)
} }
Some(_) => panic!("Error receiving packet"), usb_ctap_hid::SendOrRecvStatus::Sent => {
None => None, panic!("Returned transmit status on receive")
}
usb_ctap_hid::SendOrRecvStatus::Timeout => None,
}; };
let now = clock.try_now().unwrap(); let now = clock.try_now().unwrap();
@@ -120,34 +123,31 @@ fn main() {
// don't cause problems with timers. // don't cause problems with timers.
ctap.update_timeouts(now); ctap.update_timeouts(now);
if let Some(interface) = usb_interface { if let Some(endpoint) = usb_endpoint {
let transport = match interface { let transport = match endpoint {
usb_ctap_hid::UsbInterface::MainHid => Transport::MainHid, usb_ctap_hid::UsbEndpoint::MainHid => Transport::MainHid,
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
usb_ctap_hid::UsbInterface::VendorHid => Transport::VendorHid, usb_ctap_hid::UsbEndpoint::VendorHid => Transport::VendorHid,
}; };
let reply = ctap.process_hid_packet(&pkt_request, transport, now); let reply = ctap.process_hid_packet(&pkt_request, transport, now);
// This block handles sending packets. // This block handles sending packets.
for mut pkt_reply in reply { for mut pkt_reply in reply {
let status = usb_ctap_hid::send_or_recv_with_timeout( let status =
&mut pkt_reply, usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT, endpoint)
SEND_TIMEOUT, .flex_unwrap();
interface,
);
match status { match status {
None => { usb_ctap_hid::SendOrRecvStatus::Timeout => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Sending packet timed out", &clock); print_packet_notice("Sending packet timed out", &clock);
// TODO: reset the ctap_hid state. // TODO: reset the ctap_hid state.
// Since sending the packet timed out, we cancel this reply. // Since sending the packet timed out, we cancel this reply.
break; break;
} }
Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending packet"), usb_ctap_hid::SendOrRecvStatus::Sent => {
Some(usb_ctap_hid::SendOrRecvStatus::Sent) => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Sent packet", &clock); print_packet_notice("Sent packet", &clock);
} }
Some(usb_ctap_hid::SendOrRecvStatus::Received(_)) => { usb_ctap_hid::SendOrRecvStatus::Received(_) => {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
print_packet_notice("Received an UNEXPECTED packet", &clock); print_packet_notice("Received an UNEXPECTED packet", &clock);
// TODO: handle this unexpected packet. // TODO: handle this unexpected packet.

View File

@@ -14,10 +14,11 @@
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use crate::console::Console; use crate::console::Console;
use crate::result::TockError; use crate::result::{OutOfRangeError, TockError, TockResult};
use crate::timer::Duration; use crate::timer::Duration;
use crate::{timer, util}; use crate::{timer, util};
use core::cell::Cell; use core::cell::Cell;
use core::convert::TryFrom;
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
use core::fmt::Write; use core::fmt::Write;
use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS}; use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
@@ -65,17 +66,30 @@ pub fn setup() -> bool {
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum UsbInterface { pub enum UsbEndpoint {
MainHid = 0, MainHid = 1,
#[cfg(feature = "vendor_hid")] #[cfg(feature = "vendor_hid")]
VendorHid = 1, VendorHid = 2,
}
impl TryFrom<usize> for UsbEndpoint {
type Error = TockError;
fn try_from(endpoint_num: usize) -> Result<Self, TockError> {
match endpoint_num {
1 => Ok(UsbEndpoint::MainHid),
#[cfg(feature = "vendor_hid")]
2 => Ok(UsbEndpoint::VendorHid),
_ => Err(OutOfRangeError.into()),
}
}
} }
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum SendOrRecvStatus { pub enum SendOrRecvStatus {
Error, Timeout,
Sent, Sent,
Received(UsbInterface), Received(UsbEndpoint),
} }
/// Waits to receive a packet. /// Waits to receive a packet.
@@ -85,7 +99,7 @@ pub enum SendOrRecvStatus {
pub fn recv_with_timeout( pub fn recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
) -> Option<SendOrRecvStatus> { ) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
writeln!( writeln!(
Console::new(), Console::new(),
@@ -97,12 +111,12 @@ pub fn recv_with_timeout(
let result = recv_with_timeout_detail(buf, timeout_delay); let result = recv_with_timeout_detail(buf, timeout_delay);
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
if let Some(SendOrRecvStatus::Received(interface)) = result { if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
writeln!( writeln!(
Console::new(), Console::new(),
"Received packet = {:02x?} on interface {}", "Received packet = {:02x?} on endpoint {}",
buf as &[u8], buf as &[u8],
interface as u8, endpoint as u8,
) )
.unwrap(); .unwrap();
} }
@@ -126,8 +140,8 @@ pub fn recv_with_timeout(
pub fn send_or_recv_with_timeout( pub fn send_or_recv_with_timeout(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
interface: UsbInterface, endpoint: UsbEndpoint,
) -> Option<SendOrRecvStatus> { ) -> TockResult<SendOrRecvStatus> {
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
writeln!( writeln!(
Console::new(), Console::new(),
@@ -137,15 +151,15 @@ pub fn send_or_recv_with_timeout(
) )
.unwrap(); .unwrap();
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, interface); let result = send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
if let Some(SendOrRecvStatus::Received(received_interface)) = result { if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
writeln!( writeln!(
Console::new(), Console::new(),
"Received packet = {:02x?} on interface {}", "Received packet = {:02x?} on endpoint {}",
buf as &[u8], buf as &[u8],
received_interface as u8, received_endpoint as u8,
) )
.unwrap(); .unwrap();
} }
@@ -156,54 +170,38 @@ pub fn send_or_recv_with_timeout(
fn recv_with_timeout_detail( fn recv_with_timeout_detail(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
) -> Option<SendOrRecvStatus> { ) -> TockResult<SendOrRecvStatus> {
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf); let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
if result.is_err() {
return Some(SendOrRecvStatus::Error);
}
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction| { let mut alarm = |direction, endpoint| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::RECEIVED => { subscribe_nr::callback_status::RECEIVED => {
// TODO: set the correct interface UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i))
SendOrRecvStatus::Received(UsbInterface::MainHid)
} }
// Unknown direction or "transmitted" sent by the kernel. // Unknown direction or "transmitted" sent by the kernel.
_ => SendOrRecvStatus::Error, _ => Err(OutOfRangeError.into()),
})); }));
}; };
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>( let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
DRIVER_NUMBER, DRIVER_NUMBER,
subscribe_nr::RECEIVE, subscribe_nr::RECEIVE,
&mut alarm, &mut alarm,
); )?;
if subscription.is_err() {
return Some(SendOrRecvStatus::Error);
}
// Setup a time-out callback. // Setup a time-out callback.
let timeout_expired = Cell::new(false);
let mut timeout_callback = timer::with_callback(|_, _| { let mut timeout_callback = timer::with_callback(|_, _| {
timeout_expired.set(true); status.set(Some(Ok(SendOrRecvStatus::Timeout)));
}); });
let mut timeout = match timeout_callback.init() { let mut timeout = timeout_callback.init()?;
Ok(x) => x, let timeout_alarm = timeout.set_alarm(timeout_delay)?;
Err(_) => return Some(SendOrRecvStatus::Error),
};
let timeout_alarm = match timeout.set_alarm(timeout_delay) {
Ok(x) => x,
Err(_) => return Some(SendOrRecvStatus::Error),
};
// Trigger USB reception. // Trigger USB reception.
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0); let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
if result_code.is_err() {
return Some(SendOrRecvStatus::Error);
}
util::yieldk_for(|| status.get().is_some() || timeout_expired.get()); util::yieldk_for(|| status.get().is_some());
let status = status.get().unwrap();
// Cleanup alarm callback. // Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) { match timeout.stop_alarm(timeout_alarm) {
@@ -212,7 +210,7 @@ fn recv_with_timeout_detail(
return_code: EALREADY, return_code: EALREADY,
.. ..
})) => { })) => {
if !timeout_expired.get() { if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
writeln!( writeln!(
Console::new(), Console::new(),
@@ -230,7 +228,7 @@ fn recv_with_timeout_detail(
} }
// Cancel USB transaction if necessary. // Cancel USB transaction if necessary.
if status.get().is_none() { if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap(); writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
let result_code = let result_code =
@@ -253,63 +251,54 @@ fn recv_with_timeout_detail(
} }
} }
status.get() core::mem::drop(result);
core::mem::drop(subscription);
core::mem::drop(result_code);
status
} }
fn send_or_recv_with_timeout_detail( fn send_or_recv_with_timeout_detail(
buf: &mut [u8; 64], buf: &mut [u8; 64],
timeout_delay: Duration<isize>, timeout_delay: Duration<isize>,
// TODO: To be used as part of the syscall. endpoint: UsbEndpoint,
_interface: UsbInterface, ) -> TockResult<SendOrRecvStatus> {
) -> Option<SendOrRecvStatus> { let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?;
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf);
if result.is_err() {
return Some(SendOrRecvStatus::Error);
}
let status = Cell::new(None); let status = Cell::new(None);
let mut alarm = |direction| { let mut alarm = |direction, endpoint| {
status.set(Some(match direction { status.set(Some(match direction {
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent, subscribe_nr::callback_status::TRANSMITTED => Ok(SendOrRecvStatus::Sent),
subscribe_nr::callback_status::RECEIVED => { subscribe_nr::callback_status::RECEIVED => {
// TODO: set the correct interface UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i))
SendOrRecvStatus::Received(UsbInterface::MainHid)
} }
// Unknown direction sent by the kernel. // Unknown direction sent by the kernel.
_ => SendOrRecvStatus::Error, _ => Err(OutOfRangeError.into()),
})); }));
}; };
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>( let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
DRIVER_NUMBER, DRIVER_NUMBER,
subscribe_nr::TRANSMIT_OR_RECEIVE, subscribe_nr::TRANSMIT_OR_RECEIVE,
&mut alarm, &mut alarm,
); )?;
if subscription.is_err() {
return Some(SendOrRecvStatus::Error);
}
// Setup a time-out callback. // Setup a time-out callback.
let timeout_expired = Cell::new(false);
let mut timeout_callback = timer::with_callback(|_, _| { let mut timeout_callback = timer::with_callback(|_, _| {
timeout_expired.set(true); status.set(Some(Ok(SendOrRecvStatus::Timeout)));
}); });
let mut timeout = match timeout_callback.init() { let mut timeout = timeout_callback.init()?;
Ok(x) => x, let timeout_alarm = timeout.set_alarm(timeout_delay)?;
Err(_) => return Some(SendOrRecvStatus::Error),
};
let timeout_alarm = match timeout.set_alarm(timeout_delay) {
Ok(x) => x,
Err(_) => return Some(SendOrRecvStatus::Error),
};
// Trigger USB transmission. // Trigger USB transmission.
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0); let result_code = syscalls::command(
if result_code.is_err() { DRIVER_NUMBER,
return Some(SendOrRecvStatus::Error); command_nr::TRANSMIT_OR_RECEIVE,
} endpoint as usize,
0,
)?;
util::yieldk_for(|| status.get().is_some() || timeout_expired.get()); util::yieldk_for(|| status.get().is_some());
let status = status.get().unwrap();
// Cleanup alarm callback. // Cleanup alarm callback.
match timeout.stop_alarm(timeout_alarm) { match timeout.stop_alarm(timeout_alarm) {
@@ -318,7 +307,7 @@ fn send_or_recv_with_timeout_detail(
return_code: EALREADY, return_code: EALREADY,
.. ..
})) => { })) => {
if !timeout_expired.get() { if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "debug_ctap")] #[cfg(feature = "debug_ctap")]
writeln!( writeln!(
Console::new(), Console::new(),
@@ -336,7 +325,7 @@ fn send_or_recv_with_timeout_detail(
} }
// Cancel USB transaction if necessary. // Cancel USB transaction if necessary.
if status.get().is_none() { if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
#[cfg(feature = "verbose_usb")] #[cfg(feature = "verbose_usb")]
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap(); writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
let result_code = let result_code =
@@ -361,5 +350,8 @@ fn send_or_recv_with_timeout_detail(
writeln!(Console::new(), "Cancelled USB transaction!").unwrap(); writeln!(Console::new(), "Cancelled USB transaction!").unwrap();
} }
status.get() core::mem::drop(result);
core::mem::drop(subscription);
core::mem::drop(result_code);
status
} }