# HG changeset patch
# User Pavel Milanes (CO7WT) <pavelmc@gmail.com>
# Date 1474390774 14400
#      Tue Sep 20 12:59:34 2016 -0400
# Node ID facbfb78c6866d6a56ed1342181e066ce429f9c4
# Parent  4c39e5af27835d0f54168265c7d5439e3235003b
[PATCH][btech.py] This patch handle the bug on Linux OS for this driver, fixes #3993

Linux OSes has problems with some variants of the radios supported by this driver, Michael Wagner OE4AMW discovered that delaying the writes fixes the bug.

Further investigation revealed that for this radios the OEM software do delay the writes and even restart the download when the fault condition arose.

This patch fixes a bunch of open issues about synthoms of this bug, I will link them in the CMS to get them closed.

Michel proposed this patch that has proved to fix this bug and retry when the bug is detected, so this patch is in his name (with some cosmetics from my side)

Look in the issue thread for more details

Thanks to Michael Wagner OE4AMW.

73 Pavel CO7WT

diff -r 4c39e5af2783 -r facbfb78c686 chirp/drivers/btech.py
--- a/chirp/drivers/btech.py    Thu Sep 08 13:14:31 2016 -0400
+++ b/chirp/drivers/btech.py    Tue Sep 20 12:59:34 2016 -0400
@@ -21,6 +21,7 @@
 
 LOG = logging.getLogger(__name__)
 
+from time import sleep
 from chirp import chirp_common, directory, memmap
 from chirp import bitwise, errors, util
 from chirp.settings import RadioSettingGroup, RadioSetting, \
@@ -334,6 +335,10 @@
 # magic string for all other models
 MSTRING = "\x55\x20\x15\x09\x20\x45\x4d\x02"
 
+# This variables controls the forced delay and retry
+# mainly triggered on Linux OS
+NEEDS_DELAY = False
+RETRY_DELAYED = False
 
 def _clean_buffer(radio):
     """Cleaning the read serial buffer, hard timeout to survive an infinite
@@ -383,6 +388,12 @@
         if len(data) < amount:
             LOG.warn("Short reading %d bytes from the %d requested." %
                      (len(data), amount))
+            # This problem can be and expression of the serial being to fast for
+            # the MCU, some radios suported by this driver need to talk with
+            # a delay to the Radio, so from now own we must delay
+            # the write operations.
+            NEEDS_DELAY = True
+            LOG.debug("Delaying future writes.")
 
     except:
         raise errors.RadioError("Error reading data from radio")
@@ -396,11 +407,27 @@
     try:
         for byte in data:
             radio.pipe.write(byte)
+            # The MCU inside some of the radios supported by this driver needs
+            # a pause between each byte write, Some OS (mainly Linux ones) have
+            # very accurate time control and as a result they talk "too fast"
+            # to the radios and this get stuck in the early stages
+            #
+            # To cope with that we introduce a delay on the writes but only if
+            # we detect this problem, this was found by Michael Wagner who
+            # proposed a patch for it, well done.
+            if NEEDS_DELAY:
+                # 5msec works, but 10 msec is proved to be safe also
+                # is better to be slow and stable than fast and unstable.
+                sleep(0.010)
 
         # DEBUG
         if debug is True:
-            LOG.debug("==> (%d) bytes:\n\n%s" %
+            if NEEDS_DELAY:
+                LOG.debug("This write was delayed")
+
+            LOG.debug("==> (%d) bytes:\n\n%s %s" %
                       (len(data), util.hexprint(data)))
+
     except:
         raise errors.RadioError("Error sending data to radio")
 
@@ -435,9 +462,19 @@
     # header validation
     c, a, l = struct.unpack(">BHB", block[1:5])
     if a != addr or l != BLOCK_SIZE or c != ord("X"):
-        LOG.debug("Invalid header for block 0x%04x" % addr)
+        LOG.error("Invalid header for block 0x%04x" % addr)
         LOG.debug("CMD: %s  ADDR: %04x  SIZE: %02x" % (c, a, l))
-        raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
+
+        if not RETRY_DELAYED:
+            # first try with header problems, forcing a write delay
+            LOG.warn("Failure occured, trying once again with delay")
+            RETRY_DELAYED = True
+            NEEDS_DELAY = True
+            return False
+        else:
+            # second try, now we fail.
+            LOG.debug("This was already a retry")
+            raise errors.RadioError("Invalid header for block 0x%04x:" % addr)
 
     # return the data
     return block[5:]
@@ -613,6 +650,15 @@
         # read
         d = _recv(radio, addr)
 
+        if d == False:
+            # retry to get that block of data.
+            msg = "Previous block request failed."
+            msg += " Cleaning buffer and trying again."
+            LOG.info(msg)
+            _clean_buffer(radio)
+            d = _recv(radio, addr)
+            RETRY_DELAYED = False
+
         # aggregate the data
         data += d
 
@@ -821,7 +867,15 @@
 
     def sync_in(self):
         """Download from radio"""
-        data = _download(self)
+        try:
+            data = _download(self)
+        except errors.RadioError:
+            msg = "First download-attempt failed."
+            msg += " Retrying the whole procedure with delayed writes."
+            LOG.error(msg)
+            NEEDS_DELAY = True
+            data = _download(self)
+
         self._mmap = memmap.MemoryMap(data)
         self.process_mmap()
 
