def send_firmware(self, firmware): def try_to_recover(): if response['offset'] == 0: # Nothing to recover return expected_crc = binascii.crc32( firmware[:response['offset']]) & 0xFFFFFFFF remainder = response['offset'] % response['max_size'] if expected_crc != response['crc']: # Invalid CRC. Remove corrupted data. response[ 'offset'] -= remainder if remainder != 0 else response[ 'max_size'] response['crc'] = \ binascii.crc32(firmware[:response['offset']]) & 0xFFFFFFFF return if (remainder != 0) and (response['offset'] != len(firmware)): # Send rest of the page. try: to_send = firmware[response['offset']:response['offset'] + response['max_size'] - remainder] response['crc'] = self.__stream_data( data=to_send, crc=response['crc'], offset=response['offset']) response['offset'] += len(to_send) except ValidationException: # Remove corrupted data. response['offset'] -= remainder response['crc'] = \ binascii.crc32(firmware[:response['offset']]) & 0xFFFFFFFF return self.__execute() self._send_event(event_type=DfuEvent.PROGRESS_EVENT, progress=response['offset']) response = self.__select_data() try_to_recover() for i in range(response['offset'], len(firmware), response['max_size']): data = firmware[i:i + response['max_size']] try: self.__create_data(len(data)) response['crc'] = self.__stream_data(data=data, crc=response['crc'], offset=i) self.__execute() except ValidationException: raise NordicSemiException("Failed to send firmware") self._send_event(event_type=DfuEvent.PROGRESS_EVENT, progress=len(data))
def send_message(self, data): packet = Slip.encode(data) logger.log(TRANSPORT_LOGGING_LEVEL, 'SLIP: --> ' + str(data)) try: self.serial_port.write(packet) except SerialException as e: raise NordicSemiException( 'Writing to serial port failed: ' + str(e) + '. ' 'If MSD is enabled on the target device, try to disable it ref. ' 'https://wiki.segger.com/index.php?title=J-Link-OB_SAM3U')
def wrapper(wrapped, instance, args, kwargs): err_code = wrapped(*args, **kwargs) if err_code != expected: try: err_string = 'Error code: {}'.format(NrfError(err_code)) except ValueError: err_string = 'Error code: 0x{:04x}, {}'.format( err_code, err_code) raise NordicSemiException('Failed to {}. {}'.format( wrapped.__name__, err_string))
def open(self): super(DfuTransportSerial, self).open() try: self.serial_port = Serial(port=self.com_port, baudrate=self.baud_rate, rtscts=self.flow_control, timeout=self.timeout) self.dfu_adapter = DFUAdapter(self.serial_port) except Exception, e: raise NordicSemiException("Serial port could not be opened on {0}" + ". Reason: {1}".format(self.com_port, e.message))
def read_req(self, conn_handle, uuid): handle = self.db_conns[conn_handle].get_char_value_handle(uuid) if handle is None: raise NordicSemiException("Characteristic value handler not found") self.driver.ble_gattc_read(conn_handle, handle, 0) result = self.evt_sync[conn_handle].wait(evt=BLEEvtID.gattc_evt_read_rsp) gatt_res = result["status"] if gatt_res == BLEGattStatusCode.success: return gatt_res, result["data"] else: return gatt_res, None
def write_prep(self, conn_handle, uuid, data, offset): handle = self.db_conns[conn_handle].get_char_value_handle(uuid) if handle == None: raise NordicSemiException('Characteristic value handler not found') write_params = BLEGattcWriteParams( BLEGattWriteOperation.prepare_write_req, BLEGattExecWriteFlag.prepared_write, handle, data, offset) self.driver.ble_gattc_write(conn_handle, write_params) result = self.evt_sync[conn_handle].wait( evt=BLEEvtID.gattc_evt_write_rsp) return result['status']
def on_gatts_evt_exchange_mtu_request(self, ble_driver, conn_handle, client_mtu): try: ble_driver.ble_gatts_exchange_mtu_reply(conn_handle, self.default_mtu) except NordicSemiException as ex: raise NordicSemiException( "MTU exchange reply failed. Common causes are: " "missing att_mtu setting in ble_cfg_set, " "different config tags used in ble_cfg_set and adv_start." ) from ex
def __calculate_checksum(self): self.dfu_adapter.send_message([DfuTransportSerial.OP_CODE['CalcChecSum']]) response = self.__get_response(DfuTransportSerial.OP_CODE['CalcChecSum']) if response is None: raise NordicSemiException('Did not receive checksum response from DFU target. ' 'If MSD is enabled on the target device, try to disable it ref. ' 'https://wiki.segger.com/index.php?title=J-Link-OB_SAM3U') (offset, crc) = struct.unpack('<II', bytearray(response)) return {'offset': offset, 'crc': crc}
def unpack_package(package_path, target_dir): """ Unpacks a Nordic DFU package. :param str package_path: Path to the package :param str target_dir: Target directory to unpack the package to :return: Manifest Manifest: Returns a manifest back to the user. The manifest is a parse datamodel of the manifest found in the Nordic DFU package. """ if not os.path.isfile(package_path): raise NordicSemiException( "Package {0} not found.".format(package_path)) target_dir = os.path.abspath(target_dir) target_base_path = os.path.dirname(target_dir) if not os.path.exists(target_base_path): raise NordicSemiException( "Base path to target directory {0} does not exist.".format( target_base_path)) if not os.path.isdir(target_base_path): raise NordicSemiException( "Base path to target directory {0} is not a directory.".format( target_base_path)) if os.path.exists(target_dir): raise NordicSemiException( "Target directory {0} exists, not able to unpack to that directory.", target_dir) with ZipFile(package_path, 'r') as pkg: pkg.extractall(target_dir) with open(os.path.join(target_dir, Package.MANIFEST_FILENAME), 'r') as f: _json = f.read() """:type :str """ return Manifest.from_json(_json)
def connect(self, target_device_name, target_device_addr): """ Connect to Bootloader or Application with Buttonless Service. Args: target_device_name (str): Device name to scan for. target_device_addr (str): Device addr to scan for. """ self.target_device_name = target_device_name self.target_device_addr = target_device_addr logger.info('BLE: Scanning for {}'.format(self.target_device_name)) self.adapter.driver.ble_gap_scan_start() self.verify_stable_connection() if self.conn_handle is None: raise NordicSemiException('Timeout. Target device not found.') logger.info('BLE: Service Discovery') self.adapter.service_discovery(conn_handle=self.conn_handle) # Check if connected peer has Buttonless service. if self.adapter.db_conns[self.conn_handle].get_cccd_handle( DFUAdapter.BLE_DFU_BUTTONLESS_CHAR_UUID): self.jump_from_buttonless_mode_to_bootloader( DFUAdapter.BLE_DFU_BUTTONLESS_CHAR_UUID) elif self.adapter.db_conns[self.conn_handle].get_cccd_handle( DFUAdapter.BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID): self.jump_from_buttonless_mode_to_bootloader( DFUAdapter.BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID) if self.bonded: # For combined Updates with bonds enabled, re-encryption is needed self.encrypt() if nrf_sd_ble_api_ver >= 3: if DFUAdapter.LOCAL_ATT_MTU > ATT_MTU_DEFAULT: logger.info('BLE: Enabling longer ATT MTUs') self.att_mtu = self.adapter.att_mtu_exchange( self.conn_handle, DFUAdapter.LOCAL_ATT_MTU) logger.info('BLE: Enabling longer Data Length') max_data_length = 251 # Max data length for SD v5 data_length = self.att_mtu + 4 # ATT PDU overhead is 4 if data_length > max_data_length: data_length = max_data_length self.adapter.data_length_update(self.conn_handle, data_length) else: logger.info('BLE: Using default ATT MTU') logger.debug('BLE: Enabling Notifications') self.adapter.enable_notification(conn_handle=self.conn_handle, uuid=DFUAdapter.CP_UUID) return self.target_device_name, self.target_device_addr
def write_cmd(self, conn_handle, uuid, data, attr_handle=None): try: tx_complete = BLEEvtID.evt_tx_complete except: tx_complete = BLEEvtID.gattc_evt_write_cmd_tx_complete if attr_handle is None: attr_handle = self.db_conns[conn_handle].get_char_value_handle( uuid) if attr_handle is None: raise NordicSemiException("Characteristic value handler not found") write_params = BLEGattcWriteParams( BLEGattWriteOperation.write_cmd, BLEGattExecWriteFlag.unused, attr_handle, data, 0, ) # Send packet and skip waiting for TX-complete event. Try maximum 3 times. for _ in range(MAX_TRIES): try: response = self.driver.ble_gattc_write(conn_handle, write_params) logger.debug( "Call ble_gattc_write: response({}) write_params({})". format(response, write_params)) return except NordicSemiException as e: # Retry if NRF_ERROR_RESOURCES error code. err = str(e) if (("Error code: 19" in err) or ("NRF_ERROR_RESOURCES" in err) or ("Error code: 12292" in err) or ("BLE_ERROR_NO_TX_PACKETS" in err)): self.evt_sync[conn_handle].wait(evt=tx_complete, timeout=2) else: raise e raise NordicSemiException( "Unable to successfully call ble_gattc_write")
def write_req(self, conn_handle, uuid, data): handle = self.db_conns[conn_handle].get_char_value_handle(uuid) if handle is None: raise NordicSemiException("Characteristic value handler not found") write_params = BLEGattcWriteParams( BLEGattWriteOperation.write_req, BLEGattExecWriteFlag.unused, handle, data, 0, ) self.driver.ble_gattc_write(conn_handle, write_params) result = self.evt_sync[conn_handle].wait(evt=BLEEvtID.gattc_evt_write_rsp) return result["status"]
def jump_from_buttonless_mode_to_bootloader(self, buttonless_uuid): """ Function for going to bootloader mode from application with buttonless service. It supports both bonded and unbonded buttonless characteristics. Args: buttonless_uuid: UUID of discovered buttonless characteristic. """ if buttonless_uuid == DFUAdapter.BLE_DFU_BUTTONLESS_BONDED_CHAR_UUID: logger.info("Bonded Buttonless characteristic discovered -> Bond") self.bond() else: logger.info( "Un-bonded Buttonless characteristic discovered -> Increment target device addr" ) self.target_device_addr = "{:X}".format( int(self.target_device_addr, 16) + 1) self.target_device_addr_type.addr[-1] += 1 # Enable indication for Buttonless DFU Service self.adapter.enable_indication(self.conn_handle, buttonless_uuid) # Enable indication for Service changed Service, if present. if self.adapter.db_conns[self.conn_handle].get_char_handle( DFUAdapter.SERVICE_CHANGED_UUID): self.adapter.enable_indication(self.conn_handle, DFUAdapter.SERVICE_CHANGED_UUID) # Enter DFU mode self.adapter.write_req(self.conn_handle, buttonless_uuid, [0x01]) response = self.indication_q.get( timeout=DfuTransportBle.DEFAULT_TIMEOUT) if response[DFUAdapter.ERROR_CODE_POS] != 0x01: raise Exception("Error - Unexpected response") # Wait for buttonless peer to disconnect self.evt_sync.wait('disconnected') # Reconnect self.target_device_name = None self.adapter.driver.ble_gap_scan_start() self.verify_stable_connection() if self.conn_handle is None: raise NordicSemiException('Timeout. Target device not found.') logger.info('BLE: Connected to target') logger.debug('BLE: Service Discovery') self.adapter.service_discovery(conn_handle=self.conn_handle)
def enable_indication(self, conn_handle, uuid): cccd_list = [2, 0] handle = self.db_conns[conn_handle].get_cccd_handle(uuid) if handle == None: raise NordicSemiException('CCCD not found') write_params = BLEGattcWriteParams(BLEGattWriteOperation.write_req, BLEGattExecWriteFlag.unused, handle, cccd_list, 0) self.driver.ble_gattc_write(conn_handle, write_params) result = self.evt_sync[conn_handle].wait( evt=BLEEvtID.gattc_evt_write_rsp) return result['status']
def open(self): super(DfuTransportSerial, self).open() try: self.__ensure_bootloader() self.serial_port = Serial(port=self.com_port, baudrate=self.baud_rate, rtscts=self.flow_control, timeout=self.DEFAULT_SERIAL_PORT_TIMEOUT) self.dfu_adapter = DFUAdapter(self.serial_port) except Exception as e: raise NordicSemiException("Serial port could not be opened on {0}" ". Reason: {1}".format(self.com_port, e.strerror)) if self.do_ping: ping_success = False start = datetime.now() while (datetime.now() - start < timedelta(seconds=self.timeout) and ping_success == False): if self.__ping() == True: ping_success = True if ping_success == False: raise NordicSemiException("No ping response after opening COM port") self.__set_prn() self.__get_mtu()
def to_list(self): data_list = [] for k in self.records: data_list.append(len(self.records[k]) + 1) # add type length data_list.append(k.value) if isinstance(self.records[k], str): data_list.extend([ord(c) for c in self.records[k]]) elif isinstance(self.records[k], list): data_list.extend(self.records[k]) else: raise NordicSemiException( 'Unsupported value type: 0x{:02X}'.format( type(self.records[k]))) return data_list
def __read_error(self): self.dfu_adapter.write_control_point( [DfuTransportBle.OP_CODE['ReadError']]) response = self.__get_response(DfuTransportBle.OP_CODE['ReadError']) (err_code, size) = struct.unpack('<HH', bytearray(response)) data = response[4:] while size < len(data): try: new = self.dfu_adapter.notifications_q.get( timeout=DfuTransportBle.DEFAULT_TIMEOUT) data.extend(new) except Queue.Empty: raise NordicSemiException('Timeout Error Read') return {'err_code': err_code, 'data': data}
def display(key_file, key, format, out_file): signer = Signing() if not os.path.isfile(key_file): raise NordicSemiException("File not found: %s" % key_file) default_key = signer.load_key(key_file) if default_key: display_sec_warning() if not key: click.echo("You must specify a key with --key (pk|sk).") return if key != "pk" and key != "sk": click.echo("Invalid key type. Valid types are (pk|sk).") return if not format: click.echo("You must specify a format with --format (hex|code|pem).") return if format != "hex" and format != "code" and format != "pem" and format != "dbgcode": click.echo("Invalid format. Valid formats are (hex|code|pem).") return if format == "dbgcode": format = "code" dbg = True else: dbg = False if format == "code" and key == "sk": click.echo("Displaying the private key as code is not available.") return if key == "pk": kstr = signer.get_vk(format, dbg) elif key == "sk": kstr = "\nWARNING: Security risk! Do not share the private key.\n\n" kstr = kstr + signer.get_sk(format, dbg) if not out_file: click.echo(kstr) else: with open(out_file, "w") as kfile: kfile.write(kstr)
def wrapper(wrapped, instance, args, kwargs): logger.debug("[%s] %s%s", instance.serial_port, wrapped.__name__, args) result = wrapped(*args, **kwargs) if isinstance(result, (list, tuple)): err_code = result[0] result = result[1:] if len(result) == 1: result = result[0] else: err_code = result result = None if err_code != expected: try: err_string = 'Error code: {}'.format(NrfError(err_code)) except ValueError: err_string = 'Error code: 0x{:04x}, {}'.format(err_code, err_code) raise NordicSemiException('Failed to {}. {}'.format(wrapped.__name__, err_string), err_code) return result
def att_mtu_exchange(self, conn_handle, mtu): try: self.driver.ble_gattc_exchange_mtu_req(conn_handle, mtu) except NordicSemiException as ex: raise NordicSemiException( "MTU exchange request failed. Common causes are: " "missing att_mtu setting in ble_cfg_set, " "different config tags used in ble_cfg_set and connect.") from ex response = self.evt_sync[conn_handle].wait(evt=BLEEvtID.gattc_evt_exchange_mtu_rsp) if response is None: return self.db_conns[conn_handle].att_mtu # Use minimum of client and server mtu to ensure both sides support the value new_mtu = min(mtu, response["att_mtu"]) logger.debug(f"New ATT MTU is {new_mtu}") self.db_conns[conn_handle].att_mtu = new_mtu return new_mtu
def bond(self): """ Bond to Application with Buttonless Service. """ self.bonded = True self.setup_sec_params() self.setup_keyset() self.adapter.driver.ble_gap_authenticate(self.conn_handle, self.sec_params) self.evt_sync.wait(evt="sec_params") self.adapter.driver.ble_gap_sec_params_reply(self.conn_handle, BLEGapSecStatus.success, None, self.keyset, None) result = self.evt_sync.wait(evt="auth_status") if result != BLEGapSecStatus.success: raise NordicSemiException( "Auth Status returned error code: {}".format(result))
def send_init_packet(self, init_packet): def try_to_recover(): if response['offset'] == 0 or response['offset'] > len( init_packet): # There is no init packet or present init packet is too long. return False expected_crc = (binascii.crc32(init_packet[:response['offset']]) & 0xFFFFFFFF) if expected_crc != response['crc']: # Present init packet is invalid. return False if len(init_packet) > response['offset']: # Send missing part. try: self.__stream_data(data=init_packet[response['offset']:], crc=expected_crc, offset=response['offset']) except ValidationException: return False self.__execute() return True response = self.__select_command() assert len( init_packet) <= response['max_size'], 'Init command is too long' if try_to_recover(): return for r in range(DfuTransportBle.RETRIES_NUMBER): try: self.__create_command(len(init_packet)) self.__stream_data(data=init_packet) self.__execute() except ValidationException: pass break else: raise NordicSemiException("Failed to send init packet")
def fromhexfile(self, f, arch=None): self.hex_file = f self.ihex.fromfile(f, format='hex') # autodetect based on base address base = self.ihex.minaddr() # check the 3 possible addresses for CRC matches try: self.probe_settings(BLDFUSettings.bl_sett_51_addr) self.set_arch('NRF51') except Exception as e: try: self.probe_settings(BLDFUSettings.bl_sett_52_addr) self.set_arch('NRF52') except Exception as e: try: self.probe_settings(BLDFUSettings.bl_sett_52840_addr) self.set_arch('NRF52840') except Exception as e: raise NordicSemiException( "Failed to parse .hex file: {0}".format(e))
def enter_bootloader_mode(self, listed_device): libusb_device = self.select_device(listed_device) if libusb_device is None: raise self.no_trigger_exception(listed_device) device_handle = libusb_device.open() dfu_iface = self.get_dfu_interface_num(libusb_device) if dfu_iface is None: raise self.no_trigger_exception(listed_device) with device_handle.claimInterface(dfu_iface): arr = bytearray("0", 'utf-8') try: device_handle.controlWrite(ReqTypeOUT, DFU_DETACH_REQUEST, 0, dfu_iface, arr) except Exception as err: if "LIBUSB_ERROR_PIPE" in str(err): return raise NordicSemiException( "Device did not exit application mode after dfu was triggered. Serial number: {}, product id 0x{}, vendor id: 0x{}\n\n" .format(listed_device.serial_number, listed_device.product_id, listed_device.vendor_id))
def connect(self): logger.debug('BLE: connect: target address: 0x{}'.format(self.target_device_addr)) logger.info('BLE: Scanning...') self.adapter.driver.ble_gap_scan_start() self.conn_handle = self.evt_sync.wait('connected') if self.conn_handle is None: raise NordicSemiException('Timeout. Target device not found.') logger.info('BLE: Connected to target') logger.debug('BLE: Service Discovery') if nrf_sd_ble_api_ver >= 3: if DFUAdapter.LOCAL_ATT_MTU > ATT_MTU_DEFAULT: logger.info('BLE: Enabling longer ATT MTUs') self.att_mtu = self.adapter.att_mtu_exchange(self.conn_handle, BleSerial.LOCAL_ATT_MTU) logger.info('BLE: ATT MTU: {}'.format(self.att_mtu)) else: logger.info('BLE: Using default ATT MTU') self.adapter.service_discovery(conn_handle=self.conn_handle) logger.debug('BLE: Enabling Notifications') self.adapter.enable_notification(conn_handle=self.conn_handle, uuid=BleSerial.RX_UUID) return self.target_device_name, self.target_device_addr
def slip_decode_esc_chars(data): """Decode esc characters in a SLIP package. Replaces 0xDBDC with 0xCO and 0xDBDD with 0xDB. :return: str decoded data :type str data: data to decode """ result = [] while len(data): char = data.pop(0) if char == 0xDB: char2 = data.pop(0) if char2 == 0xDC: result.append(0xC0) elif char2 == 0xDD: result.append(0xDB) else: raise NordicSemiException( 'Char 0xDB NOT followed by 0xDC or 0xDD') else: result.append(char) return result
def calculate_crc(crc, firmware_filename): """ Calculates CRC16 has on provided firmware filename :type str firmware_filename: """ data_buffer = b'' read_size = 4096 with open(firmware_filename, 'rb') as firmware_file: while True: data = firmware_file.read(read_size) if data: data_buffer += data else: break if crc == 16: return calc_crc16(data_buffer, 0xffff) elif crc == 32: return binascii.crc32(data_buffer) else: raise NordicSemiException("Invalid CRC type")
def no_trigger_exception(self, device): return NordicSemiException( "No trigger interface found for device with serial number: {}, Product ID: 0x{} and Vendor ID: 0x{}\n" .format(device.serial_number, device.product_id, device.vendor_id))
def generate(zipfile, debug_mode, application, application_version, application_version_string, bootloader, bootloader_version, hw_version, sd_req, softdevice, key_file): """ Generate a zip package for distribution to apps that support Nordic DFU OTA. The application, bootloader, and SoftDevice files are converted to .bin if supplied as .hex files. For more information on the generated package, see: http://developer.nordicsemi.com/nRF5_SDK/doc/ The following combinations are supported by this command: * BL only: Supported. * SD only: Supported (SD of same Major Version). * APP only: Supported. * BL + SD: Supported. * BL + APP: Not supported (use two packages instead). * BL + SD + APP: Supported. * SD + APP: Supported (SD of same Major Version). """ zipfile_path = zipfile # Check combinations if bootloader is not None and application is not None and softdevice is None: click.echo( "Error: Invalid combination: use two .zip packages instead.") return if debug_mode is None: debug_mode = False # The user can specify the application version with two different # formats. As an integer, e.g. 102130, or as a string # "10.21.30". Internally we convert to integer. if application_version_string: application_version_internal = convert_version_string_to_int( application_version_string) else: application_version_internal = application_version if application_version_internal == 'none': application_version_internal = None if bootloader_version == 'none': bootloader_version = None if hw_version == 'none': hw_version = None # Convert multiple value into a single instance if len(sd_req) > 1: click.echo( "Please specify SoftDevice requirements as a comma-separated list: --sd-req 0xXXXX,0xYYYY,..." ) return elif len(sd_req) == 0: sd_req = None else: sd_req = sd_req[0] if sd_req == 'none': sd_req = None # Initial consistency checks if application_version_internal is not None and application is None: click.echo("Error: Application version with no image.") return if bootloader_version is not None and bootloader is None: click.echo("Error: Bootloader version with no image.") return if debug_mode: display_debug_warning() # Default to no version checking if application_version_internal is None: application_version_internal = Package.DEFAULT_APP_VERSION if bootloader_version is None: bootloader_version = Package.DEFAULT_BL_VERSION if hw_version is None: hw_version = Package.DEFAULT_HW_VERSION if sd_req is None: # Use string as this will be mapped into an int below sd_req = str(Package.DEFAULT_SD_REQ[0]) # Version checks if hw_version is None: click.echo("Error: --hw-version required.") return if sd_req is None: click.echo("Error: --sd-req required.") return if application is not None and application_version_internal is None: click.echo( 'Error: --application-version or --application-version-string' 'required with application image.') return if bootloader is not None and bootloader_version is None: click.echo( "Error: --bootloader-version required with bootloader image.") return sd_req_list = [] if sd_req is not None: try: # This will parse any string starting with 0x as base 16. sd_req_list = sd_req.split(',') sd_req_list = map(int_as_text_to_int, sd_req_list) except ValueError: raise NordicSemiException("Could not parse value for --sd-req. " "Hex values should be prefixed with 0x.") signer = Signing() default_key = signer.load_key(key_file) if default_key: display_sec_warning() package = Package(debug_mode, hw_version, application_version_internal, bootloader_version, sd_req_list, application, bootloader, softdevice, key_file) package.generate_package(zipfile_path) log_message = "Zip created at {0}".format(zipfile_path) click.echo(log_message)
def generate(self, arch, app_file, app_ver, bl_ver, bl_sett_ver, custom_bl_sett_addr): """ Populates the settings object based on the given parameters. :param arch: Architecture family string, e.g. NRF51 :param app_file: Path to application file :param app_ver: Application version number :param bl_ver: Bootloader version number :param bl_sett_ver: Bootloader settings version number :param custom_bl_sett_addr: Custom start address for the settings page :return: """ # Set the architecture self.set_arch(arch) if custom_bl_sett_addr is not None: self.bl_sett_addr = custom_bl_sett_addr if bl_sett_ver == 1: self.setts = BLDFUSettingsStructV1() else: raise NordicSemiException("Unknown bootloader settings version") self.bl_sett_ver = bl_sett_ver & 0xffffffff self.bl_ver = bl_ver & 0xffffffff if app_ver is not None: self.app_ver = app_ver & 0xffffffff else: self.app_ver = 0x0 & 0xffffffff if app_file is not None: # load application to find out size and CRC self.temp_dir = tempfile.mkdtemp(prefix="nrf_dfu_bl_sett_") self.app_bin = Package.normalize_firmware_to_bin(self.temp_dir, app_file) # calculate application size and CRC32 self.app_sz = int(Package.calculate_file_size(self.app_bin)) & 0xffffffff self.app_crc = int(Package.calculate_crc(32, self.app_bin)) & 0xffffffff self.bank0_bank_code = 0x1 & 0xffffffff else: self.app_sz = 0x0 & 0xffffffff self.app_crc = 0x0 & 0xffffffff self.bank0_bank_code = 0x0 & 0xffffffff # build the uint32_t array arr = [0x0] * self.setts.uint32_count # additional harcoded values self.bank_layout = 0x0 & 0xffffffff self.bank_current = 0x0 & 0xffffffff # fill in the settings arr[self.setts.offs_sett_ver] = self.bl_sett_ver arr[self.setts.offs_app_ver] = self.app_ver arr[self.setts.offs_bl_ver] = self.bl_ver arr[self.setts.offs_bank_layout] = self.bank_layout arr[self.setts.offs_bank_current] = self.bank_current arr[self.setts.offs_bank0_img_sz] = self.app_sz arr[self.setts.offs_bank0_img_crc] = self.app_crc arr[self.setts.offs_bank0_bank_code] = self.bank0_bank_code # calculate the CRC32 from the filled-in settings crc_format_str = '<' + ('I' * (self.setts.uint32_count - 1)) crc_arr = arr[1:] crc_data = struct.pack(crc_format_str, *crc_arr) self.crc = binascii.crc32(crc_data) & 0xffffffff # fill in the calculated CRC32 arr[self.setts.offs_crc] = self.crc format_str = '<' + ('I' * self.setts.uint32_count) # Get the packed data to insert into the hex instance data = struct.pack(format_str, *arr) # insert the data at the correct address self.ihex.puts(self.bl_sett_addr, data)