Interleave sending and receiving of packets to reduce rx latency (#515)
* Interleave sending and receiving of packets to reduce latency in receiving of packets * Add patch to CtapUsbSyscallDriver * Minor tweaks from review * Log when overwritting an existing reply * Only log when 'debug_ctap' is enabled * Make ctap mod public, as per review * Rename send_or_recv to send_and_maybe_recv * fix typo * Don't process packets on other transport while doing keepalive * Don't process packets on other transport while doing keepalive * More accurately determine if reply has finished * Move comment closer to appropriate location * Add tests for canceling keepalive packets * Added a TODO for kaczmarczyck re ctap module being public * remove the unnecessary sleep()s * undo messed up commit * address pylint warnings * Fix merge mess up, and patch fido2 Usage Page * Fix up completely borked merge * Remove patch to FIDO usage, after #523. * remove obsolete aspects to diff Co-authored-by: kaczmarczyck <43844792+kaczmarczyck@users.noreply.github.com>
This commit is contained in:
154
src/main.rs
154
src/main.rs
@@ -32,6 +32,7 @@ use ctap2::api::connection::{HidConnection, SendOrRecvStatus};
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use ctap2::clock::CtapClock;
|
||||
use ctap2::clock::{new_clock, Clock, ClockInt, KEEPALIVE_DELAY, KEEPALIVE_DELAY_MS};
|
||||
use ctap2::ctap::hid::HidPacketIterator;
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
use ctap2::env::tock::blink_leds;
|
||||
use ctap2::env::tock::{switch_off_leds, wink_leds, TockEnv};
|
||||
@@ -46,12 +47,72 @@ use libtock_drivers::console::Console;
|
||||
use libtock_drivers::result::FlexUnwrap;
|
||||
use libtock_drivers::timer::Duration;
|
||||
use libtock_drivers::usb_ctap_hid;
|
||||
use usb_ctap_hid::UsbEndpoint;
|
||||
|
||||
libtock_core::stack_size! {0x4000}
|
||||
|
||||
const SEND_TIMEOUT: Milliseconds<ClockInt> = Milliseconds(1000);
|
||||
const KEEPALIVE_DELAY_TOCK: Duration<isize> = Duration::from_ms(KEEPALIVE_DELAY_MS as isize);
|
||||
|
||||
#[cfg(not(feature = "vendor_hid"))]
|
||||
const NUM_ENDPOINTS: usize = 1;
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
const NUM_ENDPOINTS: usize = 2;
|
||||
|
||||
// The reply/replies that are queued for each endpoint.
|
||||
struct EndpointReply {
|
||||
endpoint: UsbEndpoint,
|
||||
transport: Transport,
|
||||
reply: HidPacketIterator,
|
||||
}
|
||||
|
||||
impl EndpointReply {
|
||||
pub fn new(endpoint: UsbEndpoint) -> Self {
|
||||
EndpointReply {
|
||||
endpoint,
|
||||
transport: match endpoint {
|
||||
UsbEndpoint::MainHid => Transport::MainHid,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
UsbEndpoint::VendorHid => Transport::VendorHid,
|
||||
},
|
||||
reply: HidPacketIterator::none(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A single packet to send.
|
||||
struct SendPacket {
|
||||
transport: Transport,
|
||||
packet: [u8; 64],
|
||||
}
|
||||
|
||||
struct EndpointReplies {
|
||||
replies: [EndpointReply; NUM_ENDPOINTS],
|
||||
}
|
||||
|
||||
impl EndpointReplies {
|
||||
pub fn new() -> Self {
|
||||
EndpointReplies {
|
||||
replies: [
|
||||
EndpointReply::new(UsbEndpoint::MainHid),
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
EndpointReply::new(UsbEndpoint::VendorHid),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
pub fn next_packet(&mut self) -> Option<SendPacket> {
|
||||
for ep in self.replies.iter_mut() {
|
||||
if let Some(packet) = ep.reply.next() {
|
||||
return Some(SendPacket {
|
||||
transport: ep.transport,
|
||||
packet,
|
||||
});
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
fn main() {
|
||||
let clock = new_clock();
|
||||
|
||||
@@ -67,6 +128,8 @@ fn main() {
|
||||
let mut led_counter = 0;
|
||||
let mut last_led_increment = boot_time;
|
||||
|
||||
let mut replies = EndpointReplies::new();
|
||||
|
||||
// Main loop. If CTAP1 is used, we register button presses for U2F while receiving and waiting.
|
||||
// The way TockOS and apps currently interact, callbacks need a yield syscall to execute,
|
||||
// making consistent blinking patterns and sending keepalives harder.
|
||||
@@ -89,21 +152,53 @@ fn main() {
|
||||
button.enable().flex_unwrap();
|
||||
}
|
||||
|
||||
// Variable for use in both the send_and_maybe_recv and recv cases.
|
||||
let mut usb_endpoint: Option<UsbEndpoint> = None;
|
||||
let mut pkt_request = [0; 64];
|
||||
let usb_endpoint =
|
||||
match usb_ctap_hid::recv_with_timeout(&mut pkt_request, KEEPALIVE_DELAY_TOCK)
|
||||
.flex_unwrap()
|
||||
{
|
||||
usb_ctap_hid::SendOrRecvStatus::Received(endpoint) => {
|
||||
|
||||
if let Some(mut packet) = replies.next_packet() {
|
||||
// send and receive.
|
||||
let hid_connection = packet.transport.hid_connection(ctap.env());
|
||||
match hid_connection.send_and_maybe_recv(&mut packet.packet, SEND_TIMEOUT) {
|
||||
Ok(SendOrRecvStatus::Timeout) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Received packet", &clock);
|
||||
Some(endpoint)
|
||||
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;
|
||||
}
|
||||
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
||||
panic!("Returned transmit status on receive")
|
||||
Ok(SendOrRecvStatus::Sent) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Sent packet", &clock);
|
||||
}
|
||||
usb_ctap_hid::SendOrRecvStatus::Timeout => None,
|
||||
};
|
||||
Ok(SendOrRecvStatus::Received(ep)) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Received another packet", &clock);
|
||||
usb_endpoint = Some(ep);
|
||||
|
||||
// Copy to incoming packet to local buffer to be consistent
|
||||
// with the receive flow.
|
||||
pkt_request = packet.packet;
|
||||
}
|
||||
Err(_) => panic!("Error sending packet"),
|
||||
}
|
||||
} else {
|
||||
// receive
|
||||
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(endpoint)
|
||||
}
|
||||
usb_ctap_hid::SendOrRecvStatus::Sent => {
|
||||
panic!("Returned transmit status on receive")
|
||||
}
|
||||
usb_ctap_hid::SendOrRecvStatus::Timeout => None,
|
||||
};
|
||||
}
|
||||
|
||||
let now = clock.try_now().unwrap();
|
||||
#[cfg(feature = "with_ctap1")]
|
||||
@@ -127,32 +222,27 @@ fn main() {
|
||||
|
||||
if let Some(endpoint) = usb_endpoint {
|
||||
let transport = match endpoint {
|
||||
usb_ctap_hid::UsbEndpoint::MainHid => Transport::MainHid,
|
||||
UsbEndpoint::MainHid => Transport::MainHid,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
usb_ctap_hid::UsbEndpoint::VendorHid => Transport::VendorHid,
|
||||
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 hid_connection = transport.hid_connection(ctap.env());
|
||||
match hid_connection.send_or_recv_with_timeout(&mut pkt_reply, SEND_TIMEOUT) {
|
||||
Ok(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.
|
||||
if reply.has_data() {
|
||||
// Update endpoint with the reply.
|
||||
for ep in replies.replies.iter_mut() {
|
||||
if ep.endpoint == endpoint {
|
||||
if ep.reply.has_data() {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Warning overwriting existing reply for endpoint {}",
|
||||
endpoint as usize
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
ep.reply = reply;
|
||||
break;
|
||||
}
|
||||
Ok(SendOrRecvStatus::Sent) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Sent packet", &clock);
|
||||
}
|
||||
Ok(SendOrRecvStatus::Received) => {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
print_packet_notice("Received an UNEXPECTED packet", &clock);
|
||||
// TODO: handle this unexpected packet.
|
||||
}
|
||||
Err(_) => panic!("Error sending packet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user