Skip to content

Commit

Permalink
Allow HidApi to refresh
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
brianp committed Aug 23, 2024
1 parent 835cd6a commit 2b1e14e
Show file tree
Hide file tree
Showing 2 changed files with 43 additions and 9 deletions.
3 changes: 3 additions & 0 deletions applications/minotari_ledger_wallet/comms/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
49 changes: 40 additions & 9 deletions applications/minotari_ledger_wallet/comms/src/ledger_wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand All @@ -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<TransportNativeHID, LedgerDeviceError> {
let hid = hidapi()?;
let transport = TransportNativeHID::new(hid).map_err(|e| LedgerDeviceError::NativeTransport(e.to_string()))?;
Ok(transport)
struct HidManager {
inner: Option<HidApi>,
}

impl HidManager {
fn new() -> Result<Self, LedgerDeviceError> {
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<Result<HidApi, String>> =
Lazy::new(|| HidApi::new().map_err(|e| format!("Unable to get HIDAPI: {}", e)));
static HID_MANAGER: Lazy<Mutex<HidManager>> =
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<TransportNativeHID, LedgerDeviceError> {
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)]
Expand Down

0 comments on commit 2b1e14e

Please sign in to comment.