From f571751d203cfcf2456dc0d85b724cbab3d4075a Mon Sep 17 00:00:00 2001 From: Daniel Mueller Date: Sat, 8 Apr 2017 18:21:57 -0700 Subject: Retry send/receive on failure It is possible that sending or receiving a feature report fails. Such failures can have multiple reasons, including transient problems. In the case of such a transient failure we should retry the operation. This change accomplishes this feat by retrying a send in case of a failure up to a maximum of three times, with a small wait in between. It also introduces the logic or receiving a response (which is not yet evaluated fully). --- nitrocli/src/main.rs | 64 ++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 59 insertions(+), 5 deletions(-) diff --git a/nitrocli/src/main.rs b/nitrocli/src/main.rs index a1f026e..6b39ad2 100644 --- a/nitrocli/src/main.rs +++ b/nitrocli/src/main.rs @@ -32,6 +32,7 @@ mod nitrokey; mod pinentry; use error::Error; +use std::mem; use std::process; use std::result; use std::thread; @@ -41,6 +42,8 @@ type Result = result::Result; type NitroFunc = Fn(&mut libhid::Handle) -> Result<()>; +const SEND_TRY_COUNT: i8 = 3; +const RECV_TRY_COUNT: i8 = 40; const SEND_RECV_DELAY_MS: u64 = 200; @@ -48,8 +51,25 @@ const SEND_RECV_DELAY_MS: u64 = 200; fn send

(handle: &mut libhid::Handle, report: &nitrokey::Report

) -> Result<()> where P: AsRef<[u8]>, { - handle.feature().send_to(0, report.as_ref())?; - return Ok(()); + let mut retry = SEND_TRY_COUNT; + loop { + let result = handle.feature().send_to(0, report.as_ref()); + retry -= 1; + + match result { + Ok(_) => { + return Ok(()); + }, + Err(err) => { + if retry > 0 { + thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS)); + continue; + } else { + return Err(Error::HidError(err)); + } + }, + } + } } @@ -57,9 +77,43 @@ fn send

(handle: &mut libhid::Handle, report: &nitrokey::Report

) -> Result< fn receive

(handle: &mut libhid::Handle) -> Result> where P: AsRef<[u8]> + Default, { - let mut report = nitrokey::Report::

::new(); - handle.feature().get_from(0, report.as_mut())?; - return Ok(report); + let mut retry = RECV_TRY_COUNT; + loop { + let mut report = nitrokey::Report::

::new(); + let result = handle.feature().get_from(0, report.as_mut()); + + retry -= 1; + + match result { + Ok(size) => { + if size < mem::size_of_val(&report) { + if retry > 0 { + continue; + } else { + return Err(Error::Error("Failed to receive complete report".to_string())); + } + } + + if !report.is_valid() { + if retry > 0 { + continue; + } else { + return Err(Error::Error("Failed to receive report: CRC mismatch".to_string())); + } + } + return Ok(report); + }, + + Err(err) => { + if retry > 0 { + thread::sleep(time::Duration::from_millis(SEND_RECV_DELAY_MS)); + continue; + } else { + return Err(Error::HidError(err)); + } + }, + } + } } -- cgit v1.2.3