From 1d53f3c92117f7c7c5920d0f38ab0f36f88c66ea Mon Sep 17 00:00:00 2001 From: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com> Date: Thu, 16 Jun 2022 17:56:44 +0200 Subject: [PATCH] Adapts libtock drivers for vendor HID (#500) * adapts libtock drivers for vendor HID * status with timeout --- patches/tock/11-usb-endpoint-receive.patch | 13 ++ src/env/tock/mod.rs | 18 +- src/main.rs | 40 ++--- .../libtock-drivers/src/usb_ctap_hid.rs | 158 +++++++++--------- 4 files changed, 117 insertions(+), 112 deletions(-) create mode 100644 patches/tock/11-usb-endpoint-receive.patch diff --git a/patches/tock/11-usb-endpoint-receive.patch b/patches/tock/11-usb-endpoint-receive.patch new file mode 100644 index 0000000..105149f --- /dev/null +++ b/patches/tock/11-usb-endpoint-receive.patch @@ -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)); + } + } + } diff --git a/src/env/tock/mod.rs b/src/env/tock/mod.rs index a835fc8..014c654 100644 --- a/src/env/tock/mod.rs +++ b/src/env/tock/mod.rs @@ -118,27 +118,27 @@ fn send_keepalive_up_needed( channel: Channel, timeout: Duration, ) -> Result<(), Ctap2StatusCode> { - let (interface, cid) = match channel { - Channel::MainHid(cid) => (usb_ctap_hid::UsbInterface::MainHid, cid), + let (endpoint, cid) = match channel { + Channel::MainHid(cid) => (usb_ctap_hid::UsbEndpoint::MainHid, cid), #[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); 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 { - None => { + usb_ctap_hid::SendOrRecvStatus::Timeout => { debug_ctap!(env, "Sending a KEEPALIVE packet timed out"); // TODO: abort user presence test? } - Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending KEEPALIVE packet"), - Some(usb_ctap_hid::SendOrRecvStatus::Sent) => { + usb_ctap_hid::SendOrRecvStatus::Sent => { 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. 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!( env, "Received a packet on channel ID {:?} while sending a KEEPALIVE packet", diff --git a/src/main.rs b/src/main.rs index 53a4e90..c1f35b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,6 @@ use embedded_time::duration::Milliseconds; use libtock_drivers::buttons::{self, ButtonState}; #[cfg(feature = "debug_ctap")] use libtock_drivers::console::Console; -#[cfg(feature = "with_ctap1")] use libtock_drivers::result::FlexUnwrap; use libtock_drivers::timer::Duration; use libtock_drivers::usb_ctap_hid; @@ -89,15 +88,19 @@ fn main() { } let mut pkt_request = [0; 64]; - let usb_interface = - match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) { - Some(usb_ctap_hid::SendOrRecvStatus::Received(interface)) => { + let usb_endpoint = + match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK) + .flex_unwrap() + { + usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => { #[cfg(feature = "debug_ctap")] print_packet_notice("Received packet", &clock); - Some(interface) + Some(endpoint) } - Some(_) => panic!("Error receiving packet"), - None => None, + usb_ctap_hid::SendOrRecvStatus::Sent => { + panic!("Returned transmit status on receive") + } + usb_ctap_hid::SendOrRecvStatus::Timeout => None, }; let now = clock.try_now().unwrap(); @@ -120,34 +123,31 @@ fn main() { // don't cause problems with timers. ctap.update_timeouts(now); - if let Some(interface) = usb_interface { - let transport = match interface { - usb_ctap_hid::UsbInterface::MainHid => Transport::MainHid, + if let Some(endpoint) = usb_endpoint { + let transport = match endpoint { + usb_ctap_hid::UsbEndpoint::MainHid => Transport::MainHid, #[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); // This block handles sending packets. for mut pkt_reply in reply { - let status = usb_ctap_hid::send_or_recv_with_timeout( - &mut pkt_reply, - SEND_TIMEOUT, - interface, - ); + let status = + usb_ctap_hid::send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT, endpoint) + .flex_unwrap(); match status { - None => { + usb_ctap_hid::SendOrRecvStatus::Timeout => { #[cfg(feature = "debug_ctap")] print_packet_notice("Sending packet timed out", &clock); // TODO: reset the ctap_hid state. // Since sending the packet timed out, we cancel this reply. break; } - Some(usb_ctap_hid::SendOrRecvStatus::Error) => panic!("Error sending packet"), - Some(usb_ctap_hid::SendOrRecvStatus::Sent) => { + usb_ctap_hid::SendOrRecvStatus::Sent => { #[cfg(feature = "debug_ctap")] print_packet_notice("Sent packet", &clock); } - Some(usb_ctap_hid::SendOrRecvStatus::Received(_)) => { + usb_ctap_hid::SendOrRecvStatus::Received(_) => { #[cfg(feature = "debug_ctap")] print_packet_notice("Received an UNEXPECTED packet", &clock); // TODO: handle this unexpected packet. diff --git a/third_party/libtock-drivers/src/usb_ctap_hid.rs b/third_party/libtock-drivers/src/usb_ctap_hid.rs index 15b0a47..fbc59f0 100644 --- a/third_party/libtock-drivers/src/usb_ctap_hid.rs +++ b/third_party/libtock-drivers/src/usb_ctap_hid.rs @@ -14,10 +14,11 @@ #[cfg(feature = "debug_ctap")] use crate::console::Console; -use crate::result::TockError; +use crate::result::{OutOfRangeError, TockError, TockResult}; use crate::timer::Duration; use crate::{timer, util}; use core::cell::Cell; +use core::convert::TryFrom; #[cfg(feature = "debug_ctap")] use core::fmt::Write; use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS}; @@ -65,17 +66,30 @@ pub fn setup() -> bool { } #[derive(Clone, Copy, PartialEq, Eq)] -pub enum UsbInterface { - MainHid = 0, +pub enum UsbEndpoint { + MainHid = 1, #[cfg(feature = "vendor_hid")] - VendorHid = 1, + VendorHid = 2, +} + +impl TryFrom for UsbEndpoint { + type Error = TockError; + + fn try_from(endpoint_num: usize) -> Result { + match endpoint_num { + 1 => Ok(UsbEndpoint::MainHid), + #[cfg(feature = "vendor_hid")] + 2 => Ok(UsbEndpoint::VendorHid), + _ => Err(OutOfRangeError.into()), + } + } } #[derive(Clone, Copy, PartialEq, Eq)] pub enum SendOrRecvStatus { - Error, + Timeout, Sent, - Received(UsbInterface), + Received(UsbEndpoint), } /// Waits to receive a packet. @@ -85,7 +99,7 @@ pub enum SendOrRecvStatus { pub fn recv_with_timeout( buf: &mut [u8; 64], timeout_delay: Duration, -) -> Option { +) -> TockResult { #[cfg(feature = "verbose_usb")] writeln!( Console::new(), @@ -97,12 +111,12 @@ pub fn recv_with_timeout( let result = recv_with_timeout_detail(buf, timeout_delay); #[cfg(feature = "verbose_usb")] - if let Some(SendOrRecvStatus::Received(interface)) = result { + if let Ok(SendOrRecvStatus::Received(endpoint)) = result { writeln!( Console::new(), - "Received packet = {:02x?} on interface {}", + "Received packet = {:02x?} on endpoint {}", buf as &[u8], - interface as u8, + endpoint as u8, ) .unwrap(); } @@ -126,8 +140,8 @@ pub fn recv_with_timeout( pub fn send_or_recv_with_timeout( buf: &mut [u8; 64], timeout_delay: Duration, - interface: UsbInterface, -) -> Option { + endpoint: UsbEndpoint, +) -> TockResult { #[cfg(feature = "verbose_usb")] writeln!( Console::new(), @@ -137,15 +151,15 @@ pub fn send_or_recv_with_timeout( ) .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")] - if let Some(SendOrRecvStatus::Received(received_interface)) = result { + if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result { writeln!( Console::new(), - "Received packet = {:02x?} on interface {}", + "Received packet = {:02x?} on endpoint {}", buf as &[u8], - received_interface as u8, + received_endpoint as u8, ) .unwrap(); } @@ -156,54 +170,38 @@ pub fn send_or_recv_with_timeout( fn recv_with_timeout_detail( buf: &mut [u8; 64], timeout_delay: Duration, -) -> Option { - let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf); - if result.is_err() { - return Some(SendOrRecvStatus::Error); - } +) -> TockResult { + let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?; let status = Cell::new(None); - let mut alarm = |direction| { + let mut alarm = |direction, endpoint| { status.set(Some(match direction { subscribe_nr::callback_status::RECEIVED => { - // TODO: set the correct interface - SendOrRecvStatus::Received(UsbInterface::MainHid) + UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i)) } // Unknown direction or "transmitted" sent by the kernel. - _ => SendOrRecvStatus::Error, + _ => Err(OutOfRangeError.into()), })); }; - let subscription = syscalls::subscribe::( + let subscription = syscalls::subscribe::( DRIVER_NUMBER, subscribe_nr::RECEIVE, &mut alarm, - ); - if subscription.is_err() { - return Some(SendOrRecvStatus::Error); - } + )?; // Setup a time-out callback. - let timeout_expired = Cell::new(false); let mut timeout_callback = timer::with_callback(|_, _| { - timeout_expired.set(true); + status.set(Some(Ok(SendOrRecvStatus::Timeout))); }); - let mut timeout = match timeout_callback.init() { - Ok(x) => x, - Err(_) => return Some(SendOrRecvStatus::Error), - }; - let timeout_alarm = match timeout.set_alarm(timeout_delay) { - Ok(x) => x, - Err(_) => return Some(SendOrRecvStatus::Error), - }; + let mut timeout = timeout_callback.init()?; + let timeout_alarm = timeout.set_alarm(timeout_delay)?; // Trigger USB reception. - let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0); - if result_code.is_err() { - return Some(SendOrRecvStatus::Error); - } + let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 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. match timeout.stop_alarm(timeout_alarm) { @@ -212,7 +210,7 @@ fn recv_with_timeout_detail( return_code: EALREADY, .. })) => { - if !timeout_expired.get() { + if matches!(status, Ok(SendOrRecvStatus::Timeout)) { #[cfg(feature = "debug_ctap")] writeln!( Console::new(), @@ -230,7 +228,7 @@ fn recv_with_timeout_detail( } // Cancel USB transaction if necessary. - if status.get().is_none() { + if matches!(status, Ok(SendOrRecvStatus::Timeout)) { #[cfg(feature = "verbose_usb")] writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap(); 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( buf: &mut [u8; 64], timeout_delay: Duration, - // TODO: To be used as part of the syscall. - _interface: UsbInterface, -) -> Option { - let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf); - if result.is_err() { - return Some(SendOrRecvStatus::Error); - } + endpoint: UsbEndpoint, +) -> TockResult { + let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?; let status = Cell::new(None); - let mut alarm = |direction| { + let mut alarm = |direction, endpoint| { 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 => { - // TODO: set the correct interface - SendOrRecvStatus::Received(UsbInterface::MainHid) + UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i)) } // Unknown direction sent by the kernel. - _ => SendOrRecvStatus::Error, + _ => Err(OutOfRangeError.into()), })); }; - let subscription = syscalls::subscribe::( + let subscription = syscalls::subscribe::( DRIVER_NUMBER, subscribe_nr::TRANSMIT_OR_RECEIVE, &mut alarm, - ); - if subscription.is_err() { - return Some(SendOrRecvStatus::Error); - } + )?; // Setup a time-out callback. - let timeout_expired = Cell::new(false); let mut timeout_callback = timer::with_callback(|_, _| { - timeout_expired.set(true); + status.set(Some(Ok(SendOrRecvStatus::Timeout))); }); - let mut timeout = match timeout_callback.init() { - Ok(x) => x, - Err(_) => return Some(SendOrRecvStatus::Error), - }; - let timeout_alarm = match timeout.set_alarm(timeout_delay) { - Ok(x) => x, - Err(_) => return Some(SendOrRecvStatus::Error), - }; + let mut timeout = timeout_callback.init()?; + let timeout_alarm = timeout.set_alarm(timeout_delay)?; // Trigger USB transmission. - let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0); - if result_code.is_err() { - return Some(SendOrRecvStatus::Error); - } + let result_code = syscalls::command( + DRIVER_NUMBER, + 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. match timeout.stop_alarm(timeout_alarm) { @@ -318,7 +307,7 @@ fn send_or_recv_with_timeout_detail( return_code: EALREADY, .. })) => { - if !timeout_expired.get() { + if matches!(status, Ok(SendOrRecvStatus::Timeout)) { #[cfg(feature = "debug_ctap")] writeln!( Console::new(), @@ -336,7 +325,7 @@ fn send_or_recv_with_timeout_detail( } // Cancel USB transaction if necessary. - if status.get().is_none() { + if matches!(status, Ok(SendOrRecvStatus::Timeout)) { #[cfg(feature = "verbose_usb")] writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap(); let result_code = @@ -361,5 +350,8 @@ fn send_or_recv_with_timeout_detail( writeln!(Console::new(), "Cancelled USB transaction!").unwrap(); } - status.get() + core::mem::drop(result); + core::mem::drop(subscription); + core::mem::drop(result_code); + status }