From 2b1e14edf206798f261ec6b412342b4d1502004c Mon Sep 17 00:00:00 2001 From: brianp Date: Fri, 23 Aug 2024 18:11:03 +0200 Subject: [PATCH] Allow HidApi to refresh If the HidApi disconnects, the instance will still exist but can no longer be used. We need a new instance, but only a single one can exist at a time. So we're locking it in a mutex to ensure mutable safety. Then if at some point communication errors because the instance is stale we will drop the existing instance by re-assignment of the inner struct field, and create a new working instance. --- .../minotari_ledger_wallet/comms/src/error.rs | 3 ++ .../comms/src/ledger_wallet.rs | 49 +++++++++++++++---- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/applications/minotari_ledger_wallet/comms/src/error.rs b/applications/minotari_ledger_wallet/comms/src/error.rs index 50d0a5c3b4..4a042f992f 100644 --- a/applications/minotari_ledger_wallet/comms/src/error.rs +++ b/applications/minotari_ledger_wallet/comms/src/error.rs @@ -30,6 +30,9 @@ pub enum LedgerDeviceError { /// HID API error #[error("HID API error `{0}`")] HidApi(String), + /// HID API error + #[error("HID API error refresh `{0}`")] + HidApiRefresh(String), /// Native HID transport error #[error("Native HID transport error `{0}`")] NativeTransport(String), diff --git a/applications/minotari_ledger_wallet/comms/src/ledger_wallet.rs b/applications/minotari_ledger_wallet/comms/src/ledger_wallet.rs index bd33ed1962..20c2e80806 100644 --- a/applications/minotari_ledger_wallet/comms/src/ledger_wallet.rs +++ b/applications/minotari_ledger_wallet/comms/src/ledger_wallet.rs @@ -20,7 +20,7 @@ // WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE // USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ops::Deref; +use std::{ops::Deref, sync::Mutex}; use ledger_transport::{APDUAnswer, APDUCommand}; use ledger_transport_hid::{hidapi::HidApi, TransportNativeHID}; @@ -34,17 +34,48 @@ pub const EXPECTED_NAME: &str = "minotari_ledger_wallet"; pub const EXPECTED_VERSION: &str = env!("CARGO_PKG_VERSION"); const WALLET_CLA: u8 = 0x80; -pub fn get_transport() -> Result { - let hid = hidapi()?; - let transport = TransportNativeHID::new(hid).map_err(|e| LedgerDeviceError::NativeTransport(e.to_string()))?; - Ok(transport) +struct HidManager { + inner: Option, +} + +impl HidManager { + fn new() -> Result { + let hidapi = HidApi::new().map_err(|e| LedgerDeviceError::HidApi(e.to_string()))?; + Ok(Self { inner: Some(hidapi) }) + } + + fn refresh_if_needed(&mut self) -> Result<(), LedgerDeviceError> { + // We need to clear out the inner HidApi instance before creating a new one + // This is because only one instance may exist, even when it no longers holds a connection, + // and we want this dropped before replacing + self.inner = None; + + self.inner = Some(HidApi::new().map_err(|e| LedgerDeviceError::HidApiRefresh(e.to_string()))?); + + Ok(()) + } + + fn get_hidapi(&self) -> Option<&HidApi> { + self.inner.as_ref() + } } -fn hidapi() -> Result<&'static HidApi, LedgerDeviceError> { - static HIDAPI: Lazy> = - Lazy::new(|| HidApi::new().map_err(|e| format!("Unable to get HIDAPI: {}", e))); +static HID_MANAGER: Lazy> = + Lazy::new(|| Mutex::new(HidManager::new().expect("Failed to initialize HidManager"))); - HIDAPI.as_ref().map_err(|e| LedgerDeviceError::HidApi(e.to_string())) +pub fn get_transport() -> Result { + let mut manager = HID_MANAGER + .lock() + .map_err(|_| LedgerDeviceError::NativeTransport("Mutex lock error".to_string()))?; + + match TransportNativeHID::new(manager.get_hidapi().unwrap()) { + Ok(transport) => Ok(transport), + Err(_) => { + manager.refresh_if_needed()?; + TransportNativeHID::new(manager.get_hidapi().unwrap()) + .map_err(|e| LedgerDeviceError::NativeTransport(e.to_string())) + }, + } } #[derive(Debug, Clone)]