def _stat_and_start_copy(path, addr, copying_state, copied_state): try: os.stat(path) except OSError: log("{} not found", path) return False log("{} update is available", path) if sum(stm.rfcore_fw_version(_FW_VERSION_WS)): # There was some WS firmware already installed. Need to remove that # before copying to flash (both FUS or WS copy require this). log("Removing existing WS firmware") _write_state(_STATE_DELETING_WS) _fus_fwdelete() else: log("Copying {} to flash", path) # Mark that the flash write has started. Any failure should result in an overall failure. _write_state( copying_state) # Either _STATE_COPYING_FUS or _STATE_COPYING_WS _copy_file_to_flash(path, addr) log("Copying complete") # The entire write has completed successfully, start the install. _write_state( copied_state) # Either _STATE_COPIED_FUS or _STATE_COPIED_WS return True
def resume(): log("Checking firmware update progress...") if stm.rfcore_status() == _MAGIC_IPCC_MEM_INCORRECT: return _write_failure_state(REASON_RFCORE_NOT_CONFIGURED) while True: state = _read_state() if state == _STATE_IDLE: log("Firmware update complete") return 0 elif state == _STATE_FAILED: log("Firmware update failed") return _read_failure_reason() # Keep calling GET_STATE until error or FUS. elif state == _STATE_WAITING_FOR_FUS: log("Querying FUS state") status, result = fus_get_state() log("FUS state: {} {}", status, result) if status == 0xFF and result == 0xFF: _write_failure_state(REASON_FUS_NOT_RESPONDING) elif status != 0: log("Operation in progress. Re-querying FUS state") elif stm.rfcore_status() == _MAGIC_FUS_ACTIVE: log("FUS active") _write_state(_STATE_CHECK_UPDATES) # Keep trying to start the WS until !fus_active() (or error). elif state == _STATE_WAITING_FOR_WS: if stm.rfcore_status() != _MAGIC_FUS_ACTIVE: log("WS active") _write_state(_STATE_IDLE) # Need to force a reset otherwise BLE will fail if FUS has changed. machine.reset() else: log("Starting WS") status, result = fus_start_ws() if status != 0: log("Can't start WS") log("WS version: {}", stm.rfcore_fw_version(_FW_VERSION_WS)) _write_failure_state(REASON_NO_WS) # Sequence the FUS 1.0.2 -> FUS 1.1.0 -> WS (depending on what's available). elif state == _STATE_CHECK_UPDATES: log("Checking for updates") fus_version = stm.rfcore_fw_version(_FW_VERSION_FUS) log("FUS version {}", fus_version) if fus_version < _FUS_VERSION_102: log("Factory FUS detected") if _stat_and_start_copy(_PATH_FUS_102, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS): continue elif fus_version >= _FUS_VERSION_102 and fus_version < _FUS_VERSION_110: log("FUS 1.0.2 detected") if _stat_and_start_copy(_PATH_FUS_110, _ADDR_FUS, _STATE_COPYING_FUS, _STATE_COPIED_FUS): continue else: log("FUS is up-to-date") if fus_version >= _FUS_VERSION_110: if _stat_and_start_copy(_PATH_WS_BLE_HCI, _ADDR_WS_BLE_HCI, _STATE_COPYING_WS, _STATE_COPIED_WS): continue else: log("No WS updates available") else: # Don't attempt to install WS if we're running an old FUS. log("Need latest FUS to install WS") # Attempt to go back to WS. # Either this will fail (because WS was removed due to FUS install), or # this whole thing was a no-op and we should be fine to restart WS. _write_state(_STATE_WAITING_FOR_WS) # This shouldn't happen - the flash write should always complete and # move straight onto the COPIED state. Failure here indicates that # the rfcore is misconfigured or the WS firmware was not deleted first. elif state == _STATE_COPYING_FUS or state == _STATE_COPYING_WS: log("Flash copy failed mid-write") _write_failure_state(REASON_FLASH_COPY_FAILED) # Flash write completed, we should immediately see GET_STATE return 0,0 # so we can start the FUS install. elif state == _STATE_COPIED_FUS: if fus_is_idle(): log("FUS copy complete, installing") _write_state(_STATE_INSTALLING_FUS) _fus_run_fwupgrade(_ADDR_FUS) else: log("FUS copy bad state") _write_failure_state(REASON_FLASH_FUS_BAD_STATE) # Keep polling the state until we see a 0,0 (success) or non-transient # error. In general we should expect to see (16,0) several times, # followed by a (255,0), followed by (0, 0). elif state == _STATE_INSTALLING_FUS: log("Installing FUS...") status, result = fus_get_state(_INSTALLING_FUS_GET_STATE_TIMEOUT) log("FUS state: {} {}", status, result) if 0x20 <= status <= 0x2F and result == 0: # FUS_STATE_FUS_UPGRD_ONGOING log("FUS still in progress...") elif 0x10 <= status <= 0x1F and result == 0x11: # FUS_STATE_FW_UPGRD_ONGOING and FUS_FW_ROLLBACK_ERROR # Confusingly this is a "FW_UPGRD" (0x10) not "FUS_UPRD" (0x20). log("Attempted to install same FUS version... re-querying FUS state to resume." ) elif status == 0: log("FUS update successful") _write_state(_STATE_CHECK_UPDATES) elif result == 0: # See below (for equivalent path for WS install -- we # sometimes see (255,0) right at the end). log("Re-querying FUS state...") elif result == 0xFF: _write_failure_state(REASON_FUS_NOT_RESPONDING_AFTER_FUS) else: _write_failure_state(REASON_FUS_VENDOR + result) # Keep polling the state until we see 0,0 or failure (1,0). Any other # result means retry (but the docs say that 0 and 1 are the only # status values). elif state == _STATE_DELETING_WS: log("Deleting WS...") status, result = fus_get_state() log("FUS state: {} {}", status, result) if status == 0: if sum(stm.rfcore_fw_version(_FW_VERSION_WS)) == 0: log("WS deletion complete") _write_state(_STATE_CHECK_UPDATES) else: log("WS deletion no effect") _write_failure_state(REASON_WS_STILL_PRESENT) elif status == 1: log("WS deletion failed") _write_failure_state(REASON_WS_DELETION_FAILED) # As for _STATE_COPIED_FUS above. We should immediately see 0,0. elif state == _STATE_COPIED_WS: if fus_is_idle(): log("WS copy complete, installing") _write_state(_STATE_INSTALLING_WS) _fus_run_fwupgrade(_ADDR_WS_BLE_HCI) else: log("WS copy bad state") _write_failure_state(REASON_FLASH_WS_BAD_STATE) # As for _STATE_INSTALLING_FUS above. elif state == _STATE_INSTALLING_WS: log("Installing WS...") status, result = fus_get_state(_INSTALLING_WS_GET_STATE_TIMEOUT) log("FUS state: {} {}", status, result) if 0x10 <= status <= 0x1F and result == 0: # FUS_STATE_FW_UPGRD_ONGOING log("WS still in progress...") elif 0x10 <= status <= 0x1F and result == 0x11: # FUS_FW_ROLLBACK_ERROR log("Attempted to install same WS version... re-querying FUS state to resume." ) elif status == 0: log("WS update successful") _write_state(_STATE_WAITING_FOR_WS) elif result in (0, 0xFE): # We get an error response with no payload sometimes at the end # of the update (this is not in AN5185). Additionally during # WS update, newer WS reports (status, result) of (255, 254) # before eventually reporting the correct state of # _STATE_INSTALLING_WS once again. In these cases, re-try the # GET_STATE. # The same thing happens transitioning from WS to FUS mode. # The actual HCI response has no payload, the result=0 comes from # _parse_vendor_response above when len=7. log("Re-querying FUS state...") elif result == 0xFF: # This is specifically a failure sending the HCI command. _write_failure_state(REASON_FUS_NOT_RESPONDING_AFTER_WS) else: _write_failure_state(REASON_WS_VENDOR + result)