From 802dd4543b7634f498bd44909461eae6d7975abb Mon Sep 17 00:00:00 2001
From: Szczepan Zalega <szczepan@nitrokey.com>
Date: Tue, 28 Feb 2017 21:02:30 +0100
Subject: Make device-level reconnect on problem with sending

Make it 3 times before throwing exception
Call hid_exit on last device disconnection

Signed-off-by: Szczepan Zalega <szczepan@nitrokey.com>
---
 device.cc        | 56 +++++++++++++++++++++++++++++++++++++++++++++++++-------
 include/device.h |  8 +++++++-
 2 files changed, 56 insertions(+), 8 deletions(-)

diff --git a/device.cc b/device.cc
index 7201087..44ec5c3 100644
--- a/device.cc
+++ b/device.cc
@@ -15,6 +15,8 @@ using namespace nitrokey::device;
 using namespace nitrokey::log;
 using namespace std::chrono;
 
+std::atomic_int Device::instances_count{0};
+
 Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
                const milliseconds send_receive_delay, const int retry_receiving_count,
                const milliseconds retry_timeout)
@@ -27,13 +29,19 @@ Device::Device(const uint16_t vid, const uint16_t pid, const DeviceModel model,
       last_command_status(0),
       m_model(model),
       m_send_receive_delay(send_receive_delay)
-{}
+{
+  instances_count++;
+}
 
 bool Device::disconnect() {
   //called in object's destructor
   LOG(__FUNCTION__, Loglevel::DEBUG_L2);
   std::lock_guard<std::mutex> lock(mex_dev_com);
-  LOG(std::string(__FUNCTION__) +  std::string(m_model==DeviceModel::PRO?"PRO":"STORAGE"), Loglevel::DEBUG_L2);
+  return _disconnect();
+}
+
+bool Device::_disconnect() {
+  LOG(std::string(__FUNCTION__) + std::string(m_model == DeviceModel::PRO ? "PRO" : "STORAGE"), Loglevel::DEBUG_L2);
   LOG(std::string(__FUNCTION__) +  std::string(" *IN* "), Loglevel::DEBUG_L2);
 
   LOG(std::string("Disconnection success: ") + std::to_string(mp_devhandle == nullptr), Loglevel::DEBUG_L2);
@@ -41,14 +49,21 @@ bool Device::disconnect() {
 
   hid_close(mp_devhandle);
   mp_devhandle = nullptr;
-  //FIXME hidexit should not be called if some devices are still active - use static active devices counter
-  //  hid_exit();
+  if (instances_count == 1){
+    LOG(std::string("Calling hid_exit"), Loglevel::DEBUG_L2);
+    hid_exit();
+  }
   return true;
 }
+
 bool Device::connect() {
   LOG(__FUNCTION__, Loglevel::DEBUG_L2);
   std::lock_guard<std::mutex> lock(mex_dev_com);
-  LOG(std::string(__FUNCTION__) +  std::string(" *IN* "), Loglevel::DEBUG_L2);
+  return _connect();
+}
+
+bool Device::_connect() {
+  LOG(std::string(__FUNCTION__) + std::string(" *IN* "), Loglevel::DEBUG_L2);
 
 //   hid_init(); // done automatically on hid_open
   mp_devhandle = hid_open(m_vid, m_pid, nullptr);
@@ -67,8 +82,16 @@ int Device::send(const void *packet) {
     throw DeviceNotConnected("Attempted HID send on an invalid descriptor.");
   }
 
-  return (hid_send_feature_report(
-      mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE));
+  int send_feature_report = -1;
+
+  for (int i = 0; i < 3 && send_feature_report < 0; ++i) {
+    send_feature_report = hid_send_feature_report(
+        mp_devhandle, (const unsigned char *)(packet), HID_REPORT_SIZE);
+    if (send_feature_report < 0) _reconnect();
+    //add thread sleep?
+    LOG(std::string("Sending attempt: ")+std::to_string(i) + " / 3" , Loglevel::DEBUG_L2);
+  }
+  return send_feature_report;
 }
 
 int Device::recv(void *packet) {
@@ -138,6 +161,23 @@ void Device::show_stats() {
   LOG(s, Loglevel::DEBUG_L2);
 }
 
+void Device::_reconnect() {
+  if (mex_dev_com.try_lock()){
+    throw std::runtime_error("mutex should be locked before entering this function");
+  }
+  LOG(__FUNCTION__, Loglevel::DEBUG_L2);
+  ++m_counters.low_level_reconnect;
+  _disconnect();
+  _connect();
+
+}
+
+Device::~Device() {
+  show_stats();
+  disconnect();
+  instances_count--;
+}
+
 Stick10::Stick10():
   Device(0x20a0, 0x4108, DeviceModel::PRO, 100ms, 5, 100ms)
   {}
@@ -167,7 +207,9 @@ std::string Device::ErrorCounters::get_as_string() {
   p(CRC_other_than_awaited);
   p(wrong_CRC);
   ss << "), ";
+  p(low_level_reconnect);
   p(sending_error);
   p(receiving_error);
   return ss.str();
 }
+#undef p
\ No newline at end of file
diff --git a/include/device.h b/include/device.h
index 5d7ee12..7b300e5 100644
--- a/include/device.h
+++ b/include/device.h
@@ -50,6 +50,7 @@ public:
     cnt busy_progressbar;
     cnt command_result_not_equal_0_recv;
     cnt communication_successful;
+    cnt low_level_reconnect;
     std::string get_as_string();
 
   } m_counters = {};
@@ -59,7 +60,7 @@ public:
                    const milliseconds send_receive_delay, const int retry_receiving_count,
                    const milliseconds retry_timeout);
 
-    virtual ~Device(){show_stats(); disconnect();}
+    virtual ~Device();
 
   // lack of device is not actually an error,
   // so it doesn't throw
@@ -97,6 +98,9 @@ public:
   DeviceModel get_device_model() const {return m_model;}
 private:
   std::atomic<uint8_t> last_command_status;
+  void _reconnect();
+  bool _connect();
+  bool _disconnect();
 
 protected:
   const uint16_t m_vid;
@@ -115,6 +119,8 @@ protected:
 
   std::atomic<hid_device *>mp_devhandle;
 
+
+  static std::atomic_int instances_count;
 };
 
 class Stick10 : public Device {
-- 
cgit v1.2.3