Skip to content

Commit

Permalink
DFU mode: improve programming robustness.
Browse files Browse the repository at this point in the history
The ctrl_transfer in dfu.get_status() might return usb.core.USBError
with errno == EPIPE, seen when called in quick succession. This is the
case when called from dfu.block_on_state(). The next call usually works.

This change tries to catch this specific error and just re-tries up to
two times in case it happens.

Without this change, the error (and programming abort) might get
undetected, as 'click' catches IOerror/EPIPE silently. This has
significant potential to brick a device if you don't check the output
and return code of 'solo' closely enough.
  • Loading branch information
enrikb committed Sep 9, 2020
1 parent 76d7255 commit 7350e45
Showing 1 changed file with 19 additions and 4 deletions.
23 changes: 19 additions & 4 deletions solo/dfu.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
# http://opensource.org/licenses/MIT>, at your option. This file may not be
# copied, modified, or distributed except according to those terms.

import errno
import struct
import time

Expand Down Expand Up @@ -137,10 +138,24 @@ def close(self,):
pass

def get_status(self,):
# bmReqType, bmReq, wValue, wIndex, data/size
s = self.dev.ctrl_transfer(
DFU.type.RECEIVE, DFU.bmReq.GETSTATUS, 0, self.intNum, 6
)
tries = 3
while True:
try:
# bmReqType, bmReq, wValue, wIndex, data/size
s = self.dev.ctrl_transfer(
DFU.type.RECEIVE, DFU.bmReq.GETSTATUS, 0, self.intNum, 6
)
break
except usb.core.USBError as e:
if e.errno == errno.EPIPE:
if tries > 0:
tries -= 1
time.sleep(0.01)
else:
# do not pass on EPIPE which might be swallowed by 'click'
raise RuntimeError("Failed to get status from DFU.")
else:
raise
return DFU.status(s)

def state(self,):
Expand Down

0 comments on commit 7350e45

Please sign in to comment.