From 2f16907c9e69fff7155aa857f1766a762373aad8 Mon Sep 17 00:00:00 2001 From: Emil Lerch Date: Thu, 20 Apr 2023 11:45:21 -0700 Subject: [PATCH] initial commit --- BLFlashCommand.py | 256 ++ libs/__init__.py | 60 + libs/base.py | 7 + libs/bflb_configobj.py | 2030 +++++++++++ libs/bflb_ecdh.py | 32 + libs/bflb_eflash_loader.py | 523 +++ libs/bflb_interface_cklink.py | 337 ++ libs/bflb_interface_eflash_loader.py | 631 ++++ libs/bflb_interface_jlink.py | 401 +++ libs/bflb_interface_openocd.py | 425 +++ libs/bflb_utils.py | 476 +++ pylink/__init__.py | 21 + pylink/binpacker.py | 57 + pylink/decorators.py | 52 + pylink/enums.py | 528 +++ pylink/errors.py | 44 + pylink/jlink.py | 4629 ++++++++++++++++++++++++++ pylink/jlock.py | 114 + pylink/library.py | 346 ++ pylink/registers.py | 206 ++ pylink/structs.py | 907 +++++ pylink/threads.py | 45 + pylink/util.py | 157 + requirements.txt | 1 + 24 files changed, 12285 insertions(+) create mode 100644 BLFlashCommand.py create mode 100644 libs/__init__.py create mode 100644 libs/base.py create mode 100644 libs/bflb_configobj.py create mode 100644 libs/bflb_ecdh.py create mode 100644 libs/bflb_eflash_loader.py create mode 100644 libs/bflb_interface_cklink.py create mode 100644 libs/bflb_interface_eflash_loader.py create mode 100644 libs/bflb_interface_jlink.py create mode 100644 libs/bflb_interface_openocd.py create mode 100644 libs/bflb_utils.py create mode 100644 pylink/__init__.py create mode 100644 pylink/binpacker.py create mode 100644 pylink/decorators.py create mode 100644 pylink/enums.py create mode 100644 pylink/errors.py create mode 100644 pylink/jlink.py create mode 100644 pylink/jlock.py create mode 100644 pylink/library.py create mode 100644 pylink/registers.py create mode 100644 pylink/structs.py create mode 100644 pylink/threads.py create mode 100644 pylink/util.py create mode 100644 requirements.txt diff --git a/BLFlashCommand.py b/BLFlashCommand.py new file mode 100644 index 0000000..0466326 --- /dev/null +++ b/BLFlashCommand.py @@ -0,0 +1,256 @@ +# uncompyle6 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: BLFlashCommand.py +""" +Created on 20221216 + +@author: Dillon +""" +import os, re, sys, argparse, binascii, threading, configparser +from libs import bflb_utils +from libs.bflb_utils import get_serial_ports +from libs.bflb_interface_eflash_loader import InterfaceEflashLoader + +def hex_to_dec(value): + try: + value = value.replace('0x', '') + int(value, 16) + return True + except ValueError: + return False + + +class ThreadIot(threading.Thread): + + def __init__(self, config, act='download', callback=None): + threading.Thread.__init__(self) + self.obj = InterfaceEflashLoader() + self.config = config + self.callback = callback + self.act = act + + def stop(self): + pass + + def run(self): + self.obj.run(self.act, self.config, self.callback) + + +class MainClass: + + def __init__(self): + self.dict_chip = { + 'BL602': ('bl602', 'bl602'), + 'BL702': ('bl702', 'bl702'), + 'BL702L': ('bl702l', 'bl702l'), + 'BL808': ('bl808', 'bl808'), + 'BL606P': ('bl606p', 'bl808'), + 'BL616': ('bl616', 'bl616')} + + def get_addr_from_partition_by_name(self, name, parition_file, index): + try: + with open(parition_file, 'rb') as (fp): + data = bytearray(fp.read()) + fp.close() + start = data.find(name.encode('utf-8')) + if start != -1: + addr = data[start + 9 + index * 4:start + 9 + 4 + index * 4] + addr.reverse() + addr = hex(int(binascii.hexlify(addr), 16)) + return (True, addr) + print(data) + print(name.encode('utf-8')) + return (False, '0') + except Exception as e: + try: + bflb_utils.printf(e) + return (False, '0') + finally: + e = None + del e + + def get_value(self, args): + self.config = dict() + self.config['param'] = dict() + self.config['check_box'] = dict() + self.config['input_path'] = dict() + self.config['param']['interface_type'] = args.interface + self.config['param']['comport_uart'] = args.port + self.config['param']['chip'] = args.chipname.lower() + if args.chipname.upper() not in self.dict_chip: + bflb_utils.printf('Error: chipname ' + args.chipname + ' is error!') + return + chip = self.dict_chip[args.chipname.upper()] + self.config['param']['chip_name'] = chip[0] + self.config['param']['chip_type'] = chip[1] + if args.interface.lower() == 'uart': + self.config['param']['speed_uart'] = str(args.baudrate) + self.config['param']['speed_jlink'] = '1000' + else: + self.config['param']['speed_uart'] = '2000000' + if str(args.baudrate) == '2000000': + self.config['param']['speed_jlink'] = '1000' + else: + self.config['param']['speed_jlink'] = str(args.baudrate) + if args.efuse: + self.config['check_box']['efuse'] = True + self.config['input_path']['efuse'] = os.path.abspath(args.efuse) + else: + self.config['check_box']['efuse'] = False + self.config['input_path']['efuse'] = '' + self.config['input_path']['config'] = args.config + try: + try: + self.erase = 1 + self.skip_mode = '0x0, 0x0' + self.boot2_isp_mode = 0 + config = configparser.ConfigParser() + if not os.path.exists(os.path.abspath(args.config)): + bflb_utils.printf('Error: Config File Not Found!') + return + config.read((os.path.abspath(args.config)), encoding='utf-8') + if config: + for item in config.sections(): + if item == 'cfg': + self.erase = config.get('cfg', 'erase', fallback=1) + self.skip_mode = config.get('cfg', 'skip_mode', fallback='0x0, 0x0') + self.boot2_isp_mode = config.get('cfg', 'boot2_isp_mode', fallback=0) + + except Exception as e: + try: + config = None + print('ConfigParser Error: ' + str(e)) + finally: + e = None + del e + + finally: + self.config['param']['erase'] = self.erase + self.config['param']['skip_mode'] = self.skip_mode + self.config['param']['boot2_isp_mode'] = self.boot2_isp_mode + + if args.key: + self.config['check_box']['encrypt'] = True + self.config['param']['aes_key'] = args.key + self.config['param']['aes_iv'] = args.iv + else: + self.config['check_box']['encrypt'] = False + self.config['param']['aes_key'] = '' + self.config['param']['aes_iv'] = '' + if args.pk: + self.config['check_box']['sign'] = True + self.config['input_path']['publickey'] = args.pk + self.config['input_path']['privatekey'] = args.sk + else: + self.config['check_box']['sign'] = False + self.config['input_path']['publickey'] = '' + self.config['input_path']['privatekey'] = '' + return self.config + + def get_value_file(self, name, path, addr, cpu_id=None): + name = str(name) + if os.path.isabs(path): + path = os.path.abspath(path) + else: + config_dir = os.path.dirname(os.path.abspath(self.config['input_path']['config'])) + path = os.path.join(config_dir, path) + if cpu_id: + path = path.replace('$(CHIPNAME)', self.config['param']['chip_name'] + '_' + cpu_id) + else: + path = path.replace('$(CHIPNAME)', self.config['param']['chip_name']) + addr = str(addr) + self.config[name] = {} + self.config[name]['addr'] = addr + self.config[name]['path'] = path + if not os.path.exists(path): + dir_path = os.path.dirname(path) + file_name = os.path.basename(path) + try: + all_file_list = os.listdir(dir_path) + except Exception as e: + try: + bflb_utils.printf(e) + return False + finally: + e = None + del e + + result = [] + if '*' in file_name: + file_name = file_name.replace('.', '\\.').replace('*', '.*[一-龥]*') + for one_name in all_file_list: + pattern = re.compile(file_name) + temp_list = pattern.findall(one_name) + if one_name in temp_list: + result += temp_list + + if len(result) > 1: + bflb_utils.printf('Error: ' + name + ' multiple files were matched! ') + return False + if len(result) == 0: + error = 'Error: ' + name + ':' + path + ' image file is not existed' + bflb_utils.printf(error) + return False + self.config[name]['path'] = os.path.join(dir_path, result[0]) + if addr.find('@partition') != -1: + bflb_utils.printf('{0} get address from partiton file {1}'.format(name, self.config['partition']['path'])) + success, addr_pt = self.get_addr_from_partition_by_name(name, self.config['partition']['path'], 0) + if not success: + bflb_utils.printf('Fail, not find ', name, ' in partition') + return False + self.config[name]['addr'] = addr_pt + bflb_utils.printf('Address=', addr_pt) + addr = addr_pt + if not hex_to_dec(addr): + error = 'Error: ' + addr + ' is invalid hexadecimal value' + bflb_utils.printf(error) + return False + return True + + def main(self, argv): + port = None + ports = [] + for item in get_serial_ports(): + ports.append(item['port']) + + if ports: + try: + port = sorted(ports, key=(lambda x: int(re.match('COM(\\d+)', x).group(1))))[0] + except Exception: + port = sorted(ports)[0] + + else: + parser = argparse.ArgumentParser(description='flash-command') + parser.add_argument('--interface', dest='interface', default='uart', help='interface to use') + parser.add_argument('--port', dest='port', default=port, help='serial port to use') + parser.add_argument('--chipname', dest='chipname', default='BL602', help='chip name') + parser.add_argument('--baudrate', dest='baudrate', default=2000000, type=int, help='the speed at which to communicate') + parser.add_argument('--config', dest='config', default='', help='run config') + parser.add_argument('--cpu_id', dest='cpu_id', default='', help='cpu id') + parser.add_argument('--efuse', dest='efuse', default='', help='efuse options') + parser.add_argument('--key', dest='key', default='', help='aes key') + parser.add_argument('--iv', dest='iv', default='', help='aes iv') + parser.add_argument('--pk', dest='pk', help='ecc public key') + parser.add_argument('--sk', dest='sk', default='', help='ecc private key') + args = parser.parse_args(argv) + if args.port: + bflb_utils.printf('Serial port is ' + args.port) + else: + if port: + bflb_utils.printf('Serial port is ' + port) + else: + bflb_utils.printf('Serial port is not found') + bflb_utils.printf('==================================================') + config = self.get_value(args) + if config: + self.obj = InterfaceEflashLoader() + self.obj.run('download', config, None) + + +if __name__ == '__main__': + print(sys.argv) + app = MainClass() + app.main(sys.argv[1:]) +# okay decompiling BLFlashCommand.pyc diff --git a/libs/__init__.py b/libs/__init__.py new file mode 100644 index 0000000..0c20ed1 --- /dev/null +++ b/libs/__init__.py @@ -0,0 +1,60 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/__init__.py +from . import bflb_utils +from . import bflb_interface_eflash_loader +from . import bflb_eflash_loader +from . import bflb_configobj +from . import bflb_interface_jlink +from . import bflb_interface_cklink +from . import bflb_interface_openocd +from .base import bflb_flash_select +from .base import bflb_efuse_boothd_create +from .base import bflb_base_eflash_loader +from .base import bflb_img_create +from .base import bflb_serial +from .base import bflb_img_loader +from base.bl602 import flash_select_do +from base.bl602 import bootheader_cfg_keys +from base.bl602 import jlink_load_cfg +from base.bl602 import cklink_load_cfg +from base.bl602 import openocd_load_cfg +from base.bl602 import img_create_do +from base.bl702 import chiptype_patch +from base.bl702 import flash_select_do +from base.bl702 import bootheader_cfg_keys +from base.bl702 import jlink_load_cfg +from base.bl702 import cklink_load_cfg +from base.bl702 import openocd_load_cfg +from base.bl702 import img_create_do +from base.bl808 import chiptype_patch +from base.bl808 import flash_select_do +from base.bl808 import bootheader_cfg_keys +from base.bl808 import jlink_load_cfg +from base.bl808 import cklink_load_cfg +from base.bl808 import openocd_load_cfg +from base.bl808 import img_create_do +from base.bl616 import chiptype_patch +from base.bl616 import flash_select_do +from base.bl616 import bootheader_cfg_keys +from base.bl616 import jlink_load_cfg +from base.bl616 import cklink_load_cfg +from base.bl616 import openocd_load_cfg +from base.bl616 import img_create_do +from base.wb03 import chiptype_patch +from base.wb03 import flash_select_do +from base.wb03 import bootheader_cfg_keys +from base.wb03 import jlink_load_cfg +from base.wb03 import cklink_load_cfg +from base.wb03 import openocd_load_cfg +from base.wb03 import img_create_do +from base.bl702l import chiptype_patch +from base.bl702l import flash_select_do +from base.bl702l import bootheader_cfg_keys +from base.bl702l import jlink_load_cfg +from base.bl702l import cklink_load_cfg +from base.bl702l import openocd_load_cfg +from base.bl702l import img_create_do +# okay decompiling ./libs/__init__.pyc diff --git a/libs/base.py b/libs/base.py new file mode 100644 index 0000000..aef52ed --- /dev/null +++ b/libs/base.py @@ -0,0 +1,7 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: - +pass +# okay decompiling ./libs/base.pyc diff --git a/libs/bflb_configobj.py b/libs/bflb_configobj.py new file mode 100644 index 0000000..ea6a213 --- /dev/null +++ b/libs/bflb_configobj.py @@ -0,0 +1,2030 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_configobj.py +import os, re, sys +from codecs import BOM_UTF8, BOM_UTF16, BOM_UTF16_BE, BOM_UTF16_LE +import six +compiler = None +BOMS = {BOM_UTF8: ('utf_8', None), + BOM_UTF16_BE: ('utf16_be', 'utf_16'), + BOM_UTF16_LE: ('utf16_le', 'utf_16'), + BOM_UTF16: ('utf_16', 'utf_16')} +BOM_LIST = { + 'utf_16': 'utf_16', + 'u16': 'utf_16', + 'utf16': 'utf_16', + 'utf-16': 'utf_16', + 'utf16_be': 'utf16_be', + 'utf_16_be': 'utf16_be', + 'utf-16be': 'utf16_be', + 'utf16_le': 'utf16_le', + 'utf_16_le': 'utf16_le', + 'utf-16le': 'utf16_le', + 'utf_8': 'utf_8', + 'u8': 'utf_8', + 'utf': 'utf_8', + 'utf8': 'utf_8', + 'utf-8': 'utf_8'} +BOM_SET = { + 'utf_8': BOM_UTF8, 'utf_16': BOM_UTF16, 'utf16_be': BOM_UTF16_BE, 'utf16_le': BOM_UTF16_LE, None: BOM_UTF8} + +def match_utf8(encoding): + return BOM_LIST.get(encoding.lower()) == 'utf_8' + + +squot = "'%s'" +dquot = '"%s"' +noquot = '%s' +wspace_plus = ' \r\n\x0b\t\'"' +tsquot = '"""%s"""' +tdquot = "'''%s'''" +MISSING = object() +__all__ = ('DEFAULT_INDENT_TYPE', 'DEFAULT_INTERPOLATION', 'ConfigObjError', 'NestingError', + 'ParseError', 'DuplicateError', 'ConfigspecError', 'ConfigObj', 'SimpleVal', + 'InterpolationError', 'InterpolationLoopError', 'MissingInterpolationOption', + 'RepeatSectionError', 'ReloadError', 'UnreprError', 'UnknownType', 'flatten_errors', + 'get_extra_values', 'BFConfigParser') +DEFAULT_INTERPOLATION = 'configparser' +DEFAULT_INDENT_TYPE = ' ' +MAX_INTERPOL_DEPTH = 10 +OPTION_DEFAULTS = { + 'interpolation': True, + 'raise_errors': False, + 'list_values': True, + 'create_empty': False, + 'file_error': False, + 'configspec': None, + 'stringify': True, + 'indent_type': None, + 'encoding': None, + 'default_encoding': None, + 'unrepr': False, + 'write_empty_values': False} + +def getObj(s): + global compiler + if compiler is None: + import compiler + s = 'a=' + s + p = compiler.parse(s) + return p.getChildren()[1].getChildren()[0].getChildren()[1] + + +class UnknownType(Exception): + pass + + +class Builder(object): + + def build(self, o): + if m is None: + raise UnknownType(o.__class__.__name__) + return m(o) + + def build_List(self, o): + return list(map(self.build, o.getChildren())) + + def build_Const(self, o): + return o.value + + def build_Dict(self, o): + d = {} + i = iter(map(self.build, o.getChildren())) + for el in i: + d[el] = next(i) + + return d + + def build_Tuple(self, o): + return tuple(self.build_List(o)) + + def build_Name(self, o): + if o.name == 'None': + return + if o.name == 'True': + return True + if o.name == 'False': + return False + raise UnknownType('Undefined Name') + + def build_Add(self, o): + real, imag = list(map(self.build_Const, o.getChildren())) + try: + real = float(real) + except TypeError: + raise UnknownType('Add') + + if not isinstance(imag, complex) or imag.real != 0.0: + raise UnknownType('Add') + return real + imag + + def build_Getattr(self, o): + parent = self.build(o.expr) + return getattr(parent, o.attrname) + + def build_UnarySub(self, o): + return -self.build_Const(o.getChildren()[0]) + + def build_UnaryAdd(self, o): + return self.build_Const(o.getChildren()[0]) + + +_builder = Builder() + +def unrepr(s): + if not s: + return s + import ast + return ast.literal_eval(s) + + +class ConfigObjError(SyntaxError): + __doc__ = '\n This is the base class for all errors that ConfigObj raises.\n It is a subclass of SyntaxError.\n ' + + def __init__(self, message='', line_number=None, line=''): + self.line = line + self.line_number = line_number + SyntaxError.__init__(self, message) + + +class NestingError(ConfigObjError): + __doc__ = "\n This error indicates a level of nesting that doesn't match.\n " + + +class ParseError(ConfigObjError): + __doc__ = '\n This error indicates that a line is badly written.\n It is neither a valid ``key = value`` line,\n nor a valid section marker line.\n ' + + +class ReloadError(IOError): + __doc__ = "\n A 'reload' operation failed.\n This exception is a subclass of ``IOError``.\n " + + def __init__(self): + IOError.__init__(self, 'reload failed, filename is not set.') + + +class DuplicateError(ConfigObjError): + __doc__ = '\n The keyword or section specified already exists.\n ' + + +class ConfigspecError(ConfigObjError): + __doc__ = '\n An error occured whilst parsing a configspec.\n ' + + +class InterpolationError(ConfigObjError): + __doc__ = 'Base class for the two interpolation errors.' + + +class InterpolationLoopError(InterpolationError): + __doc__ = 'Maximum interpolation depth exceeded in string interpolation.' + + def __init__(self, option): + InterpolationError.__init__(self, 'interpolation loop detected in value "%s".' % option) + + +class RepeatSectionError(ConfigObjError): + __doc__ = '\n This error indicates additional sections in a section with a\n ``__many__`` (repeated) section.\n ' + + +class MissingInterpolationOption(InterpolationError): + __doc__ = 'A value specified for interpolation was missing.' + + def __init__(self, option): + msg = 'missing option "%s" in interpolation.' % option + InterpolationError.__init__(self, msg) + + +class UnreprError(ConfigObjError): + __doc__ = 'An error parsing in unrepr mode.' + + +class InterpolationEngine(object): + __doc__ = '\n A helper class to help perform string interpolation.\n\n This class is an abstract base class; its descendants perform\n the actual work.\n ' + _KEYCRE = re.compile('%\\(([^)]*)\\)s') + _cookie = '%' + + def __init__(self, section): + self.section = section + + def interpolate(self, key, value): + if self._cookie not in value: + return value + + def recursive_interpolate(key, value, section, backtrail): + if ( + key, section.name) in backtrail: + raise InterpolationLoopError(key) + backtrail[(key, section.name)] = 1 + match = self._KEYCRE.search(value) + while match: + k, v, s = self._parse_match(match) + if k is None: + replacement = v + else: + replacement = recursive_interpolate(k, v, s, backtrail) + start, end = match.span() + value = ''.join((value[:start], replacement, value[end:])) + new_search_start = start + len(replacement) + match = self._KEYCRE.search(value, new_search_start) + + del backtrail[(key, section.name)] + return value + + value = recursive_interpolate(key, value, self.section, {}) + return value + + def _fetch(self, key): + """Helper function to fetch values from owning section. + + Returns a 2-tuple: the value, and the section where it was found. + """ + save_interp = self.section.main.interpolation + self.section.main.interpolation = False + current_section = self.section + while 1: + val = current_section.get(key) + if val is not None: + if not isinstance(val, Section): + break + val = current_section.get('DEFAULT', {}).get(key) + if val is not None: + if not isinstance(val, Section): + break + if current_section.parent is current_section: + break + else: + current_section = current_section.parent + + self.section.main.interpolation = save_interp + if val is None: + raise MissingInterpolationOption(key) + return (val, current_section) + + def _parse_match(self, match): + """Implementation-dependent helper function. + + Will be passed a match object corresponding to the interpolation + key we just found (e.g., "%(foo)s" or "$foo"). Should look up that + key in the appropriate config file section (using the ``_fetch()`` + helper function) and return a 3-tuple: (key, value, section) + + ``key`` is the name of the key we're looking for + ``value`` is the value found for that key + ``section`` is a reference to the section where it was found + + ``key`` and ``section`` should be None if no further + interpolation should be performed on the resulting value + (e.g., if we interpolated "$$" and returned "$"). + """ + raise NotImplementedError() + + +class ConfigParserInterpolation(InterpolationEngine): + __doc__ = 'Behaves like ConfigParser.' + _cookie = '%' + _KEYCRE = re.compile('%\\(([^)]*)\\)s') + + def _parse_match(self, match): + key = match.group(1) + value, section = self._fetch(key) + return ( + key, value, section) + + +class TemplateInterpolation(InterpolationEngine): + __doc__ = 'Behaves like string.Template.' + _cookie = '$' + _delimiter = '$' + _KEYCRE = re.compile('\n \\$(?:\n (?P\\$) | # Two $ signs\n (?P[_a-z][_a-z0-9]*) | # $name format\n {(?P[^}]*)} # ${name} format\n )\n ', re.IGNORECASE | re.VERBOSE) + + def _parse_match(self, match): + key = match.group('named') or match.group('braced') + if key is not None: + value, section = self._fetch(key) + return ( + key, value, section) + if match.group('escaped') is not None: + return ( + None, self._delimiter, None) + return ( + None, match.group(), None) + + +interpolation_engines = {'configparser':ConfigParserInterpolation, + 'template':TemplateInterpolation} + +def __newobj__(cls, *args): + return (cls.__new__)(cls, *args) + + +class Section(dict): + __doc__ = "\n A dictionary-like object that represents a section in a config file.\n \n It does string interpolation if the 'interpolation' attribute\n of the 'main' object is set to True.\n \n Interpolation is tried first from this object, then from the 'DEFAULT'\n section of this object, next from the parent and its 'DEFAULT' section,\n and so on until the main object is reached.\n \n A Section will behave like an ordered dictionary - following the\n order of the ``scalars`` and ``sections`` attributes.\n You can use this to change the order of members.\n \n Iteration follows the order: scalars, then sections.\n " + + def __setstate__(self, state): + dict.update(self, state[0]) + self.__dict__.update(state[1]) + + def __reduce__(self): + state = ( + dict(self), self.__dict__) + return ( + __newobj__, (self.__class__,), state) + + def __init__(self, parent, depth, main, indict=None, name=None): + """ + * parent is the section above + * depth is the depth level of this section + * main is the main ConfigObj + * indict is a dictionary to initialise the section with + """ + if indict is None: + indict = {} + dict.__init__(self) + self.parent = parent + self.main = main + self.depth = depth + self.name = name + self._initialise() + for entry, value in indict.items(): + self[entry] = value + + def _initialise(self): + self.scalars = [] + self.sections = [] + self.comments = {} + self.inline_comments = {} + self.configspec = None + self.defaults = [] + self.default_values = {} + self.extra_values = [] + self._created = False + + def _interpolate(self, key, value): + try: + engine = self._interpolation_engine + except AttributeError: + name = self.main.interpolation + if name == True: + name = DEFAULT_INTERPOLATION + name = name.lower() + class_ = interpolation_engines.get(name, None) + if class_ is None: + self.main.interpolation = False + return value + engine = self._interpolation_engine = class_(self) + + return engine.interpolate(key, value) + + def __getitem__(self, key): + """Fetch the item and do string interpolation.""" + val = dict.__getitem__(self, key) + if self.main.interpolation: + if isinstance(val, six.string_types): + return self._interpolate(key, val) + if isinstance(val, list): + + def _check(entry): + if isinstance(entry, six.string_types): + return self._interpolate(key, entry) + return entry + + new = [_check(entry) for entry in val] + if new != val: + return new + return val + + def __setitem__(self, key, value, unrepr=False): + """ + Correctly set a value. + + Making dictionary values Section instances. + (We have to special case 'Section' instances - which are also dicts) + + Keys must be strings. + Values need only be strings (or lists of strings) if + ``main.stringify`` is set. + + ``unrepr`` must be set when setting a value to a dictionary, without + creating a new sub-section. + """ + if not isinstance(key, six.string_types): + raise ValueError('The key "%s" is not a string.' % key) + if key not in self.comments: + self.comments[key] = [] + self.inline_comments[key] = '' + if key in self.defaults: + self.defaults.remove(key) + if isinstance(value, Section): + if key not in self: + self.sections.append(key) + dict.__setitem__(self, key, value) + else: + if isinstance(value, dict) and not unrepr: + if key not in self: + self.sections.append(key) + new_depth = self.depth + 1 + dict.__setitem__(self, key, Section(self, new_depth, (self.main), indict=value, name=key)) + else: + if key not in self: + self.scalars.append(key) + if not self.main.stringify or isinstance(value, six.string_types): + pass + else: + if isinstance(value, (list, tuple)): + for entry in value: + if not isinstance(entry, six.string_types): + raise TypeError('Value is not a string "%s".' % entry) + + else: + raise TypeError('Value is not a string "%s".' % value) + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + """Remove items from the sequence when deleting.""" + dict.__delitem__(self, key) + if key in self.scalars: + self.scalars.remove(key) + else: + self.sections.remove(key) + del self.comments[key] + del self.inline_comments[key] + + def get(self, key, default=None): + """A version of ``get`` that doesn't bypass string interpolation.""" + try: + return self[key] + except KeyError: + return default + + def update(self, indict): + """ + A version of update that uses our ``__setitem__``. + """ + for entry in indict: + self[entry] = indict[entry] + + def pop(self, key, default=MISSING): + """ + 'D.pop(k[,d]) -> v, remove specified key and return the corresponding value. + If key is not found, d is returned if given, otherwise KeyError is raised' + """ + try: + val = self[key] + except KeyError: + if default is MISSING: + raise + else: + val = default + else: + del self[key] + + return val + + def popitem(self): + """Pops the first (key,val)""" + sequence = self.scalars + self.sections + if not sequence: + raise KeyError(": 'popitem(): dictionary is empty'") + key = sequence[0] + val = self[key] + del self[key] + return ( + key, val) + + def clear(self): + """ + A version of clear that also affects scalars/sections + Also clears comments and configspec. + + Leaves other attributes alone : + depth/main/parent are not affected + """ + dict.clear(self) + self.scalars = [] + self.sections = [] + self.comments = {} + self.inline_comments = {} + self.configspec = None + self.defaults = [] + self.extra_values = [] + + def setdefault(self, key, default=None): + """A version of setdefault that sets sequence if appropriate.""" + try: + return self[key] + except KeyError: + self[key] = default + return self[key] + + def items(self): + """D.items() -> list of D's (key, value) pairs, as 2-tuples""" + return list(zip(self.scalars + self.sections, list(self.values()))) + + def keys(self): + """D.keys() -> list of D's keys""" + return self.scalars + self.sections + + def values(self): + """D.values() -> list of D's values""" + return [self[key] for key in self.scalars + self.sections] + + def iteritems(self): + """D.iteritems() -> an iterator over the (key, value) items of D""" + return iter(list(self.items())) + + def iterkeys(self): + """D.iterkeys() -> an iterator over the keys of D""" + return iter(self.scalars + self.sections) + + __iter__ = iterkeys + + def itervalues(self): + """D.itervalues() -> an iterator over the values of D""" + return iter(list(self.values())) + + def __repr__(self): + """x.__repr__() <==> repr(x)""" + + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + + return '{%s}' % ', '.join(['%s: %s' % (repr(key), repr(_getval(key))) for key in self.scalars + self.sections]) + + __str__ = __repr__ + __str__.__doc__ = 'x.__str__() <==> str(x)' + + def dict(self): + """ + Return a deepcopy of self as a dictionary. + + All members that are ``Section`` instances are recursively turned to + ordinary dictionaries - by calling their ``dict`` method. + + >>> n = a.dict() + >>> n == a + 1 + >>> n is a + 0 + """ + newdict = {} + for entry in self: + this_entry = self[entry] + if isinstance(this_entry, Section): + this_entry = this_entry.dict() + else: + if isinstance(this_entry, list): + this_entry = list(this_entry) + else: + if isinstance(this_entry, tuple): + this_entry = tuple(this_entry) + newdict[entry] = this_entry + + return newdict + + def merge(self, indict): + """ + A recursive update - useful for merging config files. + + >>> a = '''[section1] + ... option1 = True + ... [[subsection]] + ... more_options = False + ... # end of file'''.splitlines() + >>> b = '''# File is user.ini + ... [section1] + ... option1 = False + ... # end of file'''.splitlines() + >>> c1 = ConfigObj(b) + >>> c2 = ConfigObj(a) + >>> c2.merge(c1) + >>> c2 + ConfigObj({'section1': {'option1': 'False', 'subsection': {'more_options': 'False'}}}) + """ + for key, val in list(indict.items()): + if key in self: + if isinstance(self[key], dict) and isinstance(val, dict): + self[key].merge(val) + else: + self[key] = val + + def rename(self, oldkey, newkey): + """ + Change a keyname to another, without changing position in sequence. + + Implemented so that transformations can be made on keys, + as well as on values. (used by encode and decode) + + Also renames comments. + """ + if oldkey in self.scalars: + the_list = self.scalars + else: + if oldkey in self.sections: + the_list = self.sections + else: + raise KeyError('Key "%s" not found.' % oldkey) + pos = the_list.index(oldkey) + val = self[oldkey] + dict.__delitem__(self, oldkey) + dict.__setitem__(self, newkey, val) + the_list.remove(oldkey) + the_list.insert(pos, newkey) + comm = self.comments[oldkey] + inline_comment = self.inline_comments[oldkey] + del self.comments[oldkey] + del self.inline_comments[oldkey] + self.comments[newkey] = comm + self.inline_comments[newkey] = inline_comment + + def walk(self, function, raise_errors=True, call_on_sections=False, **keywargs): + """ + Walk every member and call a function on the keyword and value. + + Return a dictionary of the return values + + If the function raises an exception, raise the errror + unless ``raise_errors=False``, in which case set the return value to + ``False``. + + Any unrecognised keyword arguments you pass to walk, will be pased on + to the function you pass in. + + Note: if ``call_on_sections`` is ``True`` then - on encountering a + subsection, *first* the function is called for the *whole* subsection, + and then recurses into it's members. This means your function must be + able to handle strings, dictionaries and lists. This allows you + to change the key of subsections as well as for ordinary members. The + return value when called on the whole subsection has to be discarded. + + See the encode and decode methods for examples, including functions. + + .. admonition:: caution + + You can use ``walk`` to transform the names of members of a section + but you mustn't add or delete members. + + >>> config = '''[XXXXsection] + ... XXXXkey = XXXXvalue'''.splitlines() + >>> cfg = ConfigObj(config) + >>> cfg + ConfigObj({'XXXXsection': {'XXXXkey': 'XXXXvalue'}}) + >>> def transform(section, key): + ... val = section[key] + ... newkey = key.replace('XXXX', 'CLIENT1') + ... section.rename(key, newkey) + ... if isinstance(val, (tuple, list, dict)): + ... pass + ... else: + ... val = val.replace('XXXX', 'CLIENT1') + ... section[newkey] = val + >>> cfg.walk(transform, call_on_sections=True) + {'CLIENT1section': {'CLIENT1key': None}} + >>> cfg + ConfigObj({'CLIENT1section': {'CLIENT1key': 'CLIENT1value'}}) + """ + out = {} + for i in range(len(self.scalars)): + entry = self.scalars[i] + try: + val = function(self, entry, **keywargs) + entry = self.scalars[i] + out[entry] = val + except Exception: + if raise_errors: + raise + else: + entry = self.scalars[i] + out[entry] = False + + for i in range(len(self.sections)): + entry = self.sections[i] + if call_on_sections: + try: + function(self, entry, **keywargs) + except Exception: + if raise_errors: + raise + else: + entry = self.sections[i] + out[entry] = False + + entry = self.sections[i] + else: + out[entry] = (self[entry].walk)(function, raise_errors=raise_errors, call_on_sections=call_on_sections, **keywargs) + + return out + + def as_bool(self, key): + """ + Accepts a key as input. The corresponding value must be a string or + the objects (``True`` or 1) or (``False`` or 0). We allow 0 and 1 to + retain compatibility with Python 2.2. + + If the string is one of ``True``, ``On``, ``Yes``, or ``1`` it returns + ``True``. + + If the string is one of ``False``, ``Off``, ``No``, or ``0`` it returns + ``False``. + + ``as_bool`` is not case sensitive. + + Any other input will raise a ``ValueError``. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_bool('a') + Traceback (most recent call last): + ValueError: Value "fish" is neither True nor False + >>> a['b'] = 'True' + >>> a.as_bool('b') + 1 + >>> a['b'] = 'off' + >>> a.as_bool('b') + 0 + """ + val = self[key] + if val == True: + return True + if val == False: + return False + try: + if not isinstance(val, six.string_types): + raise KeyError() + else: + return self.main._bools[val.lower()] + except KeyError: + raise ValueError('Value "%s" is neither True nor False' % val) + + def as_int(self, key): + """ + A convenience method which coerces the specified value to an integer. + + If the value is an invalid literal for ``int``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_int('a') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: 'fish' + >>> a['b'] = '1' + >>> a.as_int('b') + 1 + >>> a['b'] = '3.2' + >>> a.as_int('b') + Traceback (most recent call last): + ValueError: invalid literal for int() with base 10: '3.2' + """ + return int(self[key]) + + def as_float(self, key): + """ + A convenience method which coerces the specified value to a float. + + If the value is an invalid literal for ``float``, a ``ValueError`` will + be raised. + + >>> a = ConfigObj() + >>> a['a'] = 'fish' + >>> a.as_float('a') #doctest: +IGNORE_EXCEPTION_DETAIL + Traceback (most recent call last): + ValueError: invalid literal for float(): fish + >>> a['b'] = '1' + >>> a.as_float('b') + 1.0 + >>> a['b'] = '3.2' + >>> a.as_float('b') #doctest: +ELLIPSIS + 3.2... + """ + return float(self[key]) + + def as_list(self, key): + """ + A convenience method which fetches the specified value, guaranteeing + that it is a list. + + >>> a = ConfigObj() + >>> a['a'] = 1 + >>> a.as_list('a') + [1] + >>> a['a'] = (1,) + >>> a.as_list('a') + [1] + >>> a['a'] = [1] + >>> a.as_list('a') + [1] + """ + result = self[key] + if isinstance(result, (tuple, list)): + return list(result) + return [result] + + def restore_default(self, key): + """ + Restore (and return) default value for the specified key. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + If there is no default value for this key, ``KeyError`` is raised. + """ + default = self.default_values[key] + dict.__setitem__(self, key, default) + if key not in self.defaults: + self.defaults.append(key) + return default + + def restore_defaults(self): + """ + Recursively restore default values to all members + that have them. + + This method will only work for a ConfigObj that was created + with a configspec and has been validated. + + It doesn't delete or modify entries without default values. + """ + for key in self.default_values: + self.restore_default(key) + + for section in self.sections: + self[section].restore_defaults() + + +class ConfigObj(Section): + __doc__ = 'An object to read, create, and write config files.' + _keyword = re.compile('^ # line start\n (\\s*) # indentation\n ( # keyword\n (?:".*?")| # double quotes\n (?:\'.*?\')| # single quotes\n (?:[^\'"=].*?) # no quotes\n )\n \\s*=\\s* # divider\n (.*) # value (including list values and comments)\n $ # line end\n ', re.VERBOSE) + _sectionmarker = re.compile('^\n (\\s*) # 1: indentation\n ((?:\\[\\s*)+) # 2: section marker open\n ( # 3: section name open\n (?:"\\s*\\S.*?\\s*")| # at least one non-space with double quotes\n (?:\'\\s*\\S.*?\\s*\')| # at least one non-space with single quotes\n (?:[^\'"\\s].*?) # at least one non-space unquoted\n ) # section name close\n ((?:\\s*\\])+) # 4: section marker close\n \\s*(\\#.*)? # 5: optional comment\n $', re.VERBOSE) + _valueexp = re.compile('^\n (?:\n (?:\n (\n (?:\n (?:\n (?:".*?")| # double quotes\n (?:\'.*?\')| # single quotes\n (?:[^\'",\\#][^,\\#]*?) # unquoted\n )\n \\s*,\\s* # comma\n )* # match all list items ending in a comma (if any)\n )\n (\n (?:".*?")| # double quotes\n (?:\'.*?\')| # single quotes\n (?:[^\'",\\#\\s][^,]*?)| # unquoted\n (?:(? 1: + msg = 'Parsing failed with several errors.\nFirst error %s' % info + error = ConfigObjError(msg) + else: + error = self._errors[0] + error.errors = self._errors + error.config = self + raise error + del self._errors + if configspec is None: + self.configspec = None + else: + self._handle_configspec(configspec) + + def _initialise(self, options=None): + if options is None: + options = OPTION_DEFAULTS + self.filename = None + self._errors = [] + self.raise_errors = options['raise_errors'] + self.interpolation = options['interpolation'] + self.list_values = options['list_values'] + self.create_empty = options['create_empty'] + self.file_error = options['file_error'] + self.stringify = options['stringify'] + self.indent_type = options['indent_type'] + self.encoding = options['encoding'] + self.default_encoding = options['default_encoding'] + self.BOM = False + self.newlines = None + self.write_empty_values = options['write_empty_values'] + self.unrepr = options['unrepr'] + self.initial_comment = [] + self.final_comment = [] + self.configspec = None + if self._inspec: + self.list_values = False + Section._initialise(self) + + def __repr__(self): + + def _getval(key): + try: + return self[key] + except MissingInterpolationOption: + return dict.__getitem__(self, key) + + return 'ConfigObj({%s})' % ', '.join(['%s: %s' % (repr(key), repr(_getval(key))) for key in self.scalars + self.sections]) + + def _handle_bom(self, infile): + """ + Handle any BOM, and decode if necessary. + + If an encoding is specified, that *must* be used - but the BOM should + still be removed (and the BOM attribute set). + + (If the encoding is wrongly specified, then a BOM for an alternative + encoding won't be discovered or removed.) + + If an encoding is not specified, UTF8 or UTF16 BOM will be detected and + removed. The BOM attribute will be set. UTF16 will be decoded to + unicode. + + NOTE: This method must not be called with an empty ``infile``. + + Specifying the *wrong* encoding is likely to cause a + ``UnicodeDecodeError``. + + ``infile`` must always be returned as a list of lines, but may be + passed in as a single string. + """ + if self.encoding is not None: + if self.encoding.lower() not in BOM_LIST: + return self._decode(infile, self.encoding) + if isinstance(infile, (list, tuple)): + line = infile[0] + else: + line = infile + if isinstance(line, six.text_type): + return self._decode(infile, self.encoding) + if self.encoding is not None: + enc = BOM_LIST[self.encoding.lower()] + if enc == 'utf_16': + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if not final_encoding: + continue + if infile.startswith(BOM): + return self._decode(infile, encoding) + + return self._decode(infile, self.encoding) + BOM = BOM_SET[enc] + if not line.startswith(BOM): + return self._decode(infile, self.encoding) + newline = line[len(BOM):] + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + self.BOM = True + return self._decode(infile, self.encoding) + for BOM, (encoding, final_encoding) in list(BOMS.items()): + if isinstance(line, six.binary_type): + if not line.startswith(BOM): + continue + else: + self.encoding = final_encoding + if not final_encoding: + self.BOM = True + newline = line[len(BOM):] + if isinstance(infile, (list, tuple)): + infile[0] = newline + else: + infile = newline + if isinstance(infile, six.text_type): + return infile.splitlines(True) + if isinstance(infile, six.binary_type): + return infile.decode('utf-8').splitlines(True) + return self._decode(infile, 'utf-8') + return self._decode(infile, encoding) + + if six.PY2: + if isinstance(line, str): + return self._decode(infile, None) + if isinstance(infile, six.binary_type): + return infile.decode('utf-8').splitlines(True) + return self._decode(infile, 'utf-8') + + def _a_to_u(self, aString): + """Decode ASCII strings to unicode if a self.encoding is specified.""" + if isinstance(aString, six.binary_type): + if self.encoding: + return aString.decode(self.encoding) + return aString + + def _decode(self, infile, encoding): + """ + Decode infile to unicode. Using the specified encoding. + + if is a string, it also needs converting to a list. + """ + if isinstance(infile, six.string_types): + return infile.splitlines(True) + if isinstance(infile, six.binary_type): + if encoding: + return infile.decode(encoding).splitlines(True) + return infile.splitlines(True) + if encoding: + for i, line in enumerate(infile): + if isinstance(line, six.binary_type): + infile[i] = line.decode(encoding) + + return infile + + def _decode_element(self, line): + """Decode element to unicode if necessary.""" + if isinstance(line, six.binary_type): + if self.default_encoding: + return line.decode(self.default_encoding) + return line + + def _str(self, value): + """ + Used by ``stringify`` within validate, to turn non-string values + into strings. + """ + if not isinstance(value, six.string_types): + return str(value) + return value + + def _parse(self, infile): + """Actually parse the config file.""" + temp_list_values = self.list_values + if self.unrepr: + self.list_values = False + comment_list = [] + done_start = False + this_section = self + maxline = len(infile) - 1 + cur_index = -1 + reset_comment = False + while cur_index < maxline: + if reset_comment: + comment_list = [] + else: + cur_index += 1 + line = infile[cur_index] + sline = line.strip() + if not sline or sline.startswith('#'): + reset_comment = False + comment_list.append(line) + continue + else: + if not done_start: + self.initial_comment = comment_list + comment_list = [] + done_start = True + reset_comment = True + mat = self._sectionmarker.match(line) + if mat is not None: + indent, sect_open, sect_name, sect_close, comment = mat.groups() + if indent: + if self.indent_type is None: + self.indent_type = indent + cur_depth = sect_open.count('[') + if cur_depth != sect_close.count(']'): + self._handle_error('Cannot compute the section depth', NestingError, infile, cur_index) + continue + if cur_depth < this_section.depth: + try: + parent = self._match_depth(this_section, cur_depth).parent + except SyntaxError: + self._handle_error('Cannot compute nesting level', NestingError, infile, cur_index) + continue + + else: + if cur_depth == this_section.depth: + parent = this_section.parent + else: + if cur_depth == this_section.depth + 1: + parent = this_section + else: + self._handle_error('Section too nested', NestingError, infile, cur_index) + continue + sect_name = self._unquote(sect_name) + if sect_name in parent: + self._handle_error('Duplicate section name', DuplicateError, infile, cur_index) + continue + else: + this_section = Section(parent, cur_depth, self, name=sect_name) + parent[sect_name] = this_section + parent.inline_comments[sect_name] = comment + parent.comments[sect_name] = comment_list + continue + else: + mat = self._keyword.match(line) + if mat is None: + self._handle_error('Invalid line ({0!r}) (matched as neither section nor keyword)'.format(line), ParseError, infile, cur_index) + else: + indent, key, value = mat.groups() + if indent: + if self.indent_type is None: + self.indent_type = indent + if value[:3] in ('"""', "'''"): + try: + value, comment, cur_index = self._multiline(value, infile, cur_index, maxline) + except SyntaxError: + self._handle_error('Parse error in multiline value', ParseError, infile, cur_index) + continue + else: + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + try: + if type(e) == UnknownType: + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing multiline value' + self._handle_error(msg, UnreprError, infile, cur_index) + continue + finally: + e = None + del e + + if self.unrepr: + comment = '' + try: + value = unrepr(value) + except Exception as e: + try: + if isinstance(e, UnknownType): + msg = 'Unknown name or type in value' + else: + msg = 'Parse error from unrepr-ing value' + self._handle_error(msg, UnreprError, infile, cur_index) + continue + finally: + e = None + del e + + else: + try: + value, comment = self._handle_value(value) + except SyntaxError: + self._handle_error('Parse error in value', ParseError, infile, cur_index) + continue + + key = self._unquote(key) + if key in this_section: + self._handle_error('Duplicate keyword name', DuplicateError, infile, cur_index) + continue + else: + this_section.__setitem__(key, value, unrepr=True) + this_section.inline_comments[key] = comment + this_section.comments[key] = comment_list + + if self.indent_type is None: + self.indent_type = '' + if not self: + self.initial_comment = self.initial_comment or comment_list + else: + pass + if not reset_comment: + self.final_comment = comment_list + self.list_values = temp_list_values + + def _match_depth(self, sect, depth): + """ + Given a section and a depth level, walk back through the sections + parents to see if the depth level matches a previous section. + + Return a reference to the right section, + or raise a SyntaxError. + """ + while depth < sect.depth: + if sect is sect.parent: + raise SyntaxError() + else: + sect = sect.parent + + if sect.depth == depth: + return sect + raise SyntaxError() + + def _handle_error(self, text, ErrorClass, infile, cur_index): + """ + Handle an error according to the error settings. + + Either raise the error or store it. + The error will have occured at ``cur_index`` + """ + line = infile[cur_index] + cur_index += 1 + message = '{0} at line {1}.'.format(text, cur_index) + error = ErrorClass(message, cur_index, line) + if self.raise_errors: + raise error + self._errors.append(error) + + def _unquote(self, value): + """Return an unquoted version of a value""" + if not value: + raise SyntaxError + if value[0] == value[-1]: + if value[0] in ('"', "'"): + value = value[1:-1] + return value + + def _quote(self, value, multiline=True): + r""" + Return a safely quoted version of a value. + + Raise a ConfigObjError if the value cannot be safely quoted. + If multiline is ``True`` (default) then use triple quotes + if necessary. + + * Don't quote values that don't need it. + * Recursively quote members of a list and return a comma joined list. + * Multiline is ``False`` for lists. + * Obey list syntax for empty and single member lists. + + If ``list_values=False`` then the value is only quoted if it contains + a ``\n`` (is multiline) or '#'. + + If ``write_empty_values`` is set, and the value is an empty string, it + won't be quoted. + """ + if multiline: + if self.write_empty_values: + if value == '': + return '' + if multiline: + if isinstance(value, (list, tuple)): + if not value: + return ',' + if len(value) == 1: + return self._quote((value[0]), multiline=False) + ',' + return ', '.join([self._quote(val, multiline=False) for val in value]) + if not isinstance(value, six.string_types): + if self.stringify: + value = str(value) + else: + raise TypeError('Value "%s" is not a string.' % value) + if not value: + return '""' + no_lists_no_quotes = not self.list_values and '\n' not in value and '#' not in value + need_triple = multiline and ("'" in value) and ('"' in value) or ('\n' in value) + hash_triple_quote = multiline and not need_triple and "'" in value and '"' in value and '#' in value + if not no_lists_no_quotes: + check_for_single = not need_triple and not hash_triple_quote + if check_for_single: + if not self.list_values: + quot = noquot + else: + if '\n' in value: + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + else: + if value[0] not in wspace_plus and value[-1] not in wspace_plus and ',' not in value: + quot = noquot + else: + quot = self._get_single_quote(value) + else: + quot = self._get_triple_quote(value) + if quot == noquot: + if '#' in value: + if self.list_values: + quot = self._get_single_quote(value) + return quot % value + + def _get_single_quote(self, value): + if "'" in value and '"' in value: + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + else: + if '"' in value: + quot = squot + else: + quot = dquot + return quot + + def _get_triple_quote(self, value): + if value.find('"""') != -1: + if value.find("'''") != -1: + raise ConfigObjError('Value "%s" cannot be safely quoted.' % value) + if value.find('"""') == -1: + quot = tdquot + else: + quot = tsquot + return quot + + def _handle_value(self, value): + """ + Given a value string, unquote, remove comment, + handle lists. (including empty and single member lists) + """ + if self._inspec: + return ( + value, '') + if not self.list_values: + mat = self._nolistvalue.match(value) + if mat is None: + raise SyntaxError() + return mat.groups() + mat = self._valueexp.match(value) + if mat is None: + raise SyntaxError() + list_values, single, empty_list, comment = mat.groups() + if list_values == '': + if single is None: + raise SyntaxError() + if empty_list is not None: + return ([], comment) + if single is not None: + if list_values and not single: + single = None + else: + single = single or '""' + single = self._unquote(single) + if list_values == '': + return ( + single, comment) + the_list = self._listvalueexp.findall(list_values) + the_list = [self._unquote(val) for val in the_list] + if single is not None: + the_list += [single] + return (the_list, comment) + + def _multiline(self, value, infile, cur_index, maxline): + """Extract the value, where we are in a multiline situation.""" + quot = value[:3] + newvalue = value[3:] + single_line = self._triple_quote[quot][0] + multi_line = self._triple_quote[quot][1] + mat = single_line.match(value) + if mat is not None: + retval = list(mat.groups()) + retval.append(cur_index) + return retval + if newvalue.find(quot) != -1: + raise SyntaxError() + while 1: + if cur_index < maxline: + cur_index += 1 + newvalue += '\n' + line = infile[cur_index] + if line.find(quot) == -1: + newvalue += line + else: + break + else: + raise SyntaxError() + + mat = multi_line.match(line) + if mat is None: + raise SyntaxError() + value, comment = mat.groups() + return ( + newvalue + value, comment, cur_index) + + def _handle_configspec(self, configspec): + """Parse the configspec.""" + if not isinstance(configspec, ConfigObj): + try: + configspec = ConfigObj(configspec, raise_errors=True, file_error=True, _inspec=True) + except ConfigObjError as e: + try: + raise ConfigspecError('Parsing configspec failed: %s' % e) + finally: + e = None + del e + + except IOError as e: + try: + raise IOError('Reading configspec failed: %s' % e) + finally: + e = None + del e + + self.configspec = configspec + + def _set_configspec(self, section, copy): + """ + Called by validate. Handles setting the configspec on subsections + including sections to be validated by __many__ + """ + configspec = section.configspec + many = configspec.get('__many__') + if isinstance(many, dict): + for entry in section.sections: + if entry not in configspec: + section[entry].configspec = many + + for entry in configspec.sections: + if entry == '__many__': + continue + elif entry not in section: + section[entry] = {} + section[entry]._created = True + if copy: + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + if isinstance(section[entry], Section): + section[entry].configspec = configspec[entry] + + def _write_line(self, indent_string, entry, this_entry, comment): + """Write an individual line, for the write method""" + if not self.unrepr: + val = self._decode_element(self._quote(this_entry)) + else: + val = repr(this_entry) + return '%s%s%s%s%s' % (indent_string, + self._decode_element(self._quote(entry, + multiline=False)), self._a_to_u(' = '), val, self._decode_element(comment)) + + def _write_marker(self, indent_string, depth, entry, comment): + """Write a section marker line""" + return '%s%s%s%s%s' % (indent_string, self._a_to_u('[' * depth), + self._quote((self._decode_element(entry)), + multiline=False), self._a_to_u(']' * depth), self._decode_element(comment)) + + def _handle_comment(self, comment): + """Deal with a comment.""" + if not comment: + return '' + start = self.indent_type + if not comment.startswith('#'): + start += self._a_to_u(' # ') + return start + comment + + def write(self, outfile=None, section=None): + """ + Write the current ConfigObj as a file + + tekNico: FIXME: use StringIO instead of real files + + >>> filename = a.filename + >>> a.filename = 'test.ini' + >>> a.write() + >>> a.filename = filename + >>> a == ConfigObj('test.ini', raise_errors=True) + 1 + >>> import os + >>> os.remove('test.ini') + """ + if self.indent_type is None: + self.indent_type = DEFAULT_INDENT_TYPE + out = [] + cs = self._a_to_u('#') + csp = self._a_to_u('# ') + if section is None: + int_val = self.interpolation + self.interpolation = False + section = self + for line in self.initial_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line: + if not stripped_line.startswith(cs): + line = csp + line + out.append(line) + + indent_string = self.indent_type * section.depth + for entry in section.scalars + section.sections: + if entry in section.defaults: + continue + else: + for comment_line in section.comments[entry]: + comment_line = self._decode_element(comment_line.lstrip()) + if comment_line: + if not comment_line.startswith(cs): + comment_line = csp + comment_line + out.append(indent_string + comment_line) + + this_entry = section[entry] + comment = self._handle_comment(section.inline_comments[entry]) + if isinstance(this_entry, Section): + out.append(self._write_marker(indent_string, this_entry.depth, entry, comment)) + out.extend(self.write(section=this_entry)) + else: + out.append(self._write_line(indent_string, entry, this_entry, comment)) + + if section is self: + for line in self.final_comment: + line = self._decode_element(line) + stripped_line = line.strip() + if stripped_line: + if not stripped_line.startswith(cs): + line = csp + line + out.append(line) + + self.interpolation = int_val + if section is not self: + return out + if self.filename is None: + if outfile is None: + if self.encoding: + out = [l.encode(self.encoding) for l in out] + if self.BOM: + if self.encoding is None or BOM_LIST.get(self.encoding.lower()) == 'utf_8': + if not out: + out.append('') + out[0] = BOM_UTF8 + out[0] + return out + newline = self.newlines or os.linesep + if getattr(outfile, 'mode', None) is not None: + if outfile.mode == 'w': + if sys.platform == 'win32': + if newline == '\r\n': + newline = '\n' + output = self._a_to_u(newline).join(out) + if not output.endswith(newline): + output += newline + if isinstance(output, six.binary_type): + output_bytes = output + else: + output_bytes = output.encode(self.encoding or self.default_encoding or 'ascii') + if self.BOM: + if self.encoding is None or match_utf8(self.encoding): + output_bytes = BOM_UTF8 + output_bytes + if outfile is not None: + outfile.write(output_bytes) + else: + with open(self.filename, 'wb') as h: + h.write(output_bytes) + + def validate(self, validator, preserve_errors=False, copy=False, section=None): + """ + Test the ConfigObj against a configspec. + + It uses the ``validator`` object from *validate.py*. + + To run ``validate`` on the current ConfigObj, call: :: + + test = config.validate(validator) + + (Normally having previously passed in the configspec when the ConfigObj + was created - you can dynamically assign a dictionary of checks to the + ``configspec`` attribute of a section though). + + It returns ``True`` if everything passes, or a dictionary of + pass/fails (True/False). If every member of a subsection passes, it + will just have the value ``True``. (It also returns ``False`` if all + members fail). + + In addition, it converts the values from strings to their native + types if their checks pass (and ``stringify`` is set). + + If ``preserve_errors`` is ``True`` (``False`` is default) then instead + of a marking a fail with a ``False``, it will preserve the actual + exception object. This can contain info about the reason for failure. + For example the ``VdtValueTooSmallError`` indicates that the value + supplied was too small. If a value (or section) is missing it will + still be marked as ``False``. + + You must have the validate module to use ``preserve_errors=True``. + + You can then use the ``flatten_errors`` function to turn your nested + results dictionary into a flattened list of failures - useful for + displaying meaningful error messages. + """ + if section is None: + if self.configspec is None: + raise ValueError('No configspec supplied.') + if preserve_errors: + from validate import VdtMissingValue + self._vdtMissingValue = VdtMissingValue + section = self + if copy: + section.initial_comment = section.configspec.initial_comment + section.final_comment = section.configspec.final_comment + section.encoding = section.configspec.encoding + section.BOM = section.configspec.BOM + section.newlines = section.configspec.newlines + section.indent_type = section.configspec.indent_type + configspec = section.configspec + self._set_configspec(section, copy) + + def validate_entry(entry, spec, val, missing, ret_true, ret_false): + section.default_values.pop(entry, None) + try: + section.default_values[entry] = validator.get_default_value(configspec[entry]) + except (KeyError, AttributeError, validator.baseErrorClass): + pass + + try: + check = validator.check(spec, val, missing=missing) + except validator.baseErrorClass as e: + try: + if not preserve_errors or isinstance(e, self._vdtMissingValue): + out[entry] = False + else: + out[entry] = e + ret_false = False + ret_true = False + finally: + e = None + del e + + else: + ret_false = False + out[entry] = True + if self.stringify or missing: + if not self.stringify: + if isinstance(check, (list, tuple)): + check = [self._str(item) for item in check] + else: + if missing and check is None: + check = '' + else: + check = self._str(check) + if check != val or missing: + section[entry] = check + if not copy: + if missing: + if entry not in section.defaults: + section.defaults.append(entry) + return ( + ret_true, ret_false) + + out = {} + ret_true = True + ret_false = True + unvalidated = [k for k in section.scalars if k not in configspec] + incorrect_sections = [k for k in configspec.sections if k in section.scalars] + incorrect_scalars = [k for k in configspec.scalars if k in section.sections] + for entry in configspec.scalars: + if entry in ('__many__', '___many___'): + continue + else: + if entry not in section.scalars or entry in section.defaults: + missing = True + val = None + if not copy or entry not in section.scalars: + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + else: + missing = False + val = section[entry] + ret_true, ret_false = validate_entry(entry, configspec[entry], val, missing, ret_true, ret_false) + + many = None + if '__many__' in configspec.scalars: + many = configspec['__many__'] + else: + if '___many___' in configspec.scalars: + many = configspec['___many___'] + if many is not None: + for entry in unvalidated: + val = section[entry] + ret_true, ret_false = validate_entry(entry, many, val, False, ret_true, ret_false) + + unvalidated = [] + for entry in incorrect_scalars: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Value %r was provided as a section' % entry + out[entry] = validator.baseErrorClass(msg) + + for entry in incorrect_sections: + ret_true = False + if not preserve_errors: + out[entry] = False + else: + ret_false = False + msg = 'Section %r was provided as a single value' % entry + out[entry] = validator.baseErrorClass(msg) + + for entry in section.sections: + if section is self: + if entry == 'DEFAULT': + continue + if section[entry].configspec is None: + unvalidated.append(entry) + continue + else: + if copy: + section.comments[entry] = configspec.comments.get(entry, []) + section.inline_comments[entry] = configspec.inline_comments.get(entry, '') + check = self.validate(validator, preserve_errors=preserve_errors, copy=copy, section=(section[entry])) + out[entry] = check + if check == False: + ret_true = False + else: + if check == True: + ret_false = False + else: + ret_true = False + + section.extra_values = unvalidated + if preserve_errors: + if not section._created: + ret_false = False + if ret_false: + if preserve_errors: + if out: + ret_false = not any(out.values()) + if ret_true: + return True + if ret_false: + return False + return out + + def reset(self): + """Clear ConfigObj instance and restore to 'freshly created' state.""" + self.clear() + self._initialise() + self.configspec = None + self._original_configspec = None + + def reload(self): + """ + Reload a ConfigObj from file. + + This method raises a ``ReloadError`` if the ConfigObj doesn't have + a filename attribute pointing to a file. + """ + if not isinstance(self.filename, six.string_types): + raise ReloadError() + filename = self.filename + current_options = {} + for entry in OPTION_DEFAULTS: + if entry == 'configspec': + continue + else: + current_options[entry] = getattr(self, entry) + + configspec = self._original_configspec + current_options['configspec'] = configspec + self.clear() + self._initialise(current_options) + self._load(filename, configspec) + + +class SimpleVal(object): + __doc__ = '\n A simple validator.\n Can be used to check that all members expected are present.\n \n To use it, provide a configspec with all your members in (the value given\n will be ignored). Pass an instance of ``SimpleVal`` to the ``validate``\n method of your ``ConfigObj``. ``validate`` will return ``True`` if all\n members are present, or a dictionary with True/False meaning\n present/missing. (Whole missing sections will be replaced with ``False``)\n ' + + def __init__(self): + self.baseErrorClass = ConfigObjError + + def check(self, check, member, missing=False): + """A dummy check method, always returns the value unchanged.""" + if missing: + raise self.baseErrorClass() + return member + + +def flatten_errors(cfg, res, levels=None, results=None): + """ + An example function that will turn a nested dictionary of results + (as returned by ``ConfigObj.validate``) into a flat list. + + ``cfg`` is the ConfigObj instance being checked, ``res`` is the results + dictionary returned by ``validate``. + + (This is a recursive function, so you shouldn't use the ``levels`` or + ``results`` arguments - they are used by the function.) + + Returns a list of keys that failed. Each member of the list is a tuple:: + + ([list of sections...], key, result) + + If ``validate`` was called with ``preserve_errors=False`` (the default) + then ``result`` will always be ``False``. + + *list of sections* is a flattened list of sections that the key was found + in. + + If the section was missing (or a section was expected and a scalar provided + - or vice-versa) then key will be ``None``. + + If the value (or section) was missing then ``result`` will be ``False``. + + If ``validate`` was called with ``preserve_errors=True`` and a value + was present, but failed the check, then ``result`` will be the exception + object returned. You can use this as a string that describes the failure. + + For example *The value "3" is of the wrong type*. + """ + if levels is None: + levels = [] + results = [] + if res == True: + return sorted(results) + if res == False or isinstance(res, Exception): + results.append((levels[:], None, res)) + if levels: + levels.pop() + return sorted(results) + for key, val in list(res.items()): + if val == True: + continue + if isinstance(cfg.get(key), dict): + levels.append(key) + flatten_errors(cfg[key], val, levels, results) + continue + else: + results.append((levels[:], key, val)) + + if levels: + levels.pop() + return sorted(results) + + +def get_extra_values(conf, _prepend=()): + """ + Find all the values and sections not in the configspec from a validated + ConfigObj. + + ``get_extra_values`` returns a list of tuples where each tuple represents + either an extra section, or an extra value. + + The tuples contain two values, a tuple representing the section the value + is in and the name of the extra values. For extra values in the top level + section the first member will be an empty tuple. For values in the 'foo' + section the first member will be ``('foo',)``. For members in the 'bar' + subsection of the 'foo' section the first member will be ``('foo', 'bar')``. + + NOTE: If you call ``get_extra_values`` on a ConfigObj instance that hasn't + been validated it will return an empty list. + """ + out = [] + out.extend([(_prepend, name) for name in conf.extra_values]) + for name in conf.sections: + if name not in conf.extra_values: + out.extend(get_extra_values(conf[name], _prepend + (name,))) + + return out + + +class BFConfigParser: + cfg_infile = None + cfg_obj = ConfigObj() + + def __init__(self, file=None): + self.cfg_infile = file + self.cfg_obj = ConfigObj(self.cfg_infile) + + def read(self, file=None): + self.cfg_infile = file + self.cfg_obj = ConfigObj((self.cfg_infile), encoding='UTF8') + return self.cfg_obj + + def get(self, section, key): + ret = self.cfg_obj[section][key] + if ret == '""': + return '' + return ret + + def set(self, section, key, value): + self.cfg_obj[section][key] = str(value) + + def sections(self): + return self.cfg_obj.keys() + + def delete_section(self, section): + del self.cfg_obj[section] + + def update_section_name(self, oldsection, newsection): + _sections = self.cfg_obj.keys() + for _section in _sections: + print(_section) + if _section == oldsection: + print(self.cfg_obj[_section]) + self.cfg_obj[newsection] = self.cfg_obj[oldsection] + + self.delete_section(oldsection) + + def options(self, section): + return self.cfg_obj[section] + + def has_option(self, section, key): + _sections = self.cfg_obj.keys() + for _section in _sections: + if _section == section: + for _key in self.cfg_obj[_section]: + if _key == key: + return True + continue + else: + continue + continue + + return False + + def write(self, outfile=None, flag=None): + if outfile is None: + self.cfg_obj.filename = self.cfg_infile + else: + self.cfg_obj.filename = outfile + self.cfg_obj.write() +# okay decompiling ./libs/bflb_configobj.pyc diff --git a/libs/bflb_ecdh.py b/libs/bflb_ecdh.py new file mode 100644 index 0000000..968f9d0 --- /dev/null +++ b/libs/bflb_ecdh.py @@ -0,0 +1,32 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_ecdh.py +import binascii +from ecdsa import ECDH, NIST256p +from libs.bflb_utils import * + +class BflbEcdh(object): + + def __init__(self, curve=NIST256p): + self.ecdh = ECDH(curve) + self.local_public_key = None + self.sharedsecret = '' + + def create_public_key(self): + self.ecdh.generate_private_key() + self.local_public_key = self.ecdh.get_public_key() + ret = binascii.hexlify(self.local_public_key.to_string()).decode('utf-8') + printf('local public key:') + printf(ret) + return ret + + def create_shared_key(self, peer_pk): + self.ecdh.load_received_public_key_bytes(binascii.unhexlify(peer_pk)) + self.sharedsecret = self.ecdh.generate_sharedsecret_bytes() + ret = binascii.hexlify(self.sharedsecret).decode('utf-8') + printf('secret key:') + printf(ret) + return ret +# okay decompiling ./libs/bflb_ecdh.pyc diff --git a/libs/bflb_eflash_loader.py b/libs/bflb_eflash_loader.py new file mode 100644 index 0000000..b5d8f21 --- /dev/null +++ b/libs/bflb_eflash_loader.py @@ -0,0 +1,523 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_eflash_loader.py +from libs.bflb_utils import * +from libs.bflb_configobj import BFConfigParser +from libs.base.bflb_base_eflash_loader import BaseEflashLoader +FLASH_ERASE_SHAKE_HAND = 'Flash erase shake hand' +FLASH_LOAD_SHAKE_HAND = 'Flash load shake hand' + +class BL602EflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl602, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def set_load_function(self): + self.load_function = 1 + + def get_flash_pin(self): + return 255 + + def show_identify_fail(self): + printf('eflash loader identify flash fail!') + self.error_code_print('0043') + return False + + def set_temp_timeout(self): + self.bflb_serial_object.set_timeout(self._default_time_out) + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class BL616EflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl616, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def run_flash2(self): + """ + If chip is bl616 or wb03, it will run this function. + """ + if self.cfg.has_option('FLASH2_CFG', 'flash2_en'): + self._flash2_en = self.cfg.get('FLASH2_CFG', 'flash2_en') == 'true' + if self._flash2_en is True: + self._flash1_size = int(self.cfg.get('FLASH2_CFG', 'flash1_size')) * 1024 * 1024 + self._flash2_size = int(self.cfg.get('FLASH2_CFG', 'flash2_size')) * 1024 * 1024 + printf('flash2 set para') + flash2_pin = 0 + flash2_clock_cfg = 0 + flash2_io_mode = 0 + flash2_clk_delay = 0 + if self.cfg.get('FLASH2_CFG', 'flash2_pin'): + flash_pin_cfg = self.cfg.get('FLASH2_CFG', 'flash2_pin') + if flash_pin_cfg.startswith('0x'): + flash2_pin = int(flash_pin_cfg, 16) + else: + flash2_pin = int(flash_pin_cfg, 10) + if self.cfg.has_option('FLASH2_CFG', 'flash2_clock_cfg'): + clock_div_cfg = self.cfg.get('FLASH2_CFG', 'flash2_clock_cfg') + if clock_div_cfg.startswith('0x'): + flash2_clock_cfg = int(clock_div_cfg, 16) + else: + flash2_clock_cfg = int(clock_div_cfg, 10) + if self.cfg.has_option('FLASH2_CFG', 'flash2_io_mode'): + io_mode_cfg = self.cfg.get('FLASH2_CFG', 'flash2_io_mode') + if io_mode_cfg.startswith('0x'): + flash2_io_mode = int(io_mode_cfg, 16) + else: + flash2_io_mode = int(io_mode_cfg, 10) + if self.cfg.has_option('FLASH2_CFG', 'flash2_clock_delay'): + clk_delay_cfg = self.cfg.get('FLASH2_CFG', 'flash2_clock_delay') + if clk_delay_cfg.startswith('0x'): + flash2_clk_delay = int(clk_delay_cfg, 16) + else: + flash2_clk_delay = int(clk_delay_cfg, 10) + self.flash2_set = (flash2_pin << 0) + (flash2_clock_cfg << 8) + (flash2_io_mode << 16) + (flash2_clk_delay << 24) + if self.load_function == 2: + printf('set flash2 cfg: %X' % self.flash2_set) + ret = self.flash_set_para_main_process(self.flash2_set, bytearray(0)) + self._need_shake_hand = False + if ret is False: + return (False, self.flash_burn_retry) + ret = self.flash_switch_bank_process(1) + self._need_shake_hand = False + if ret is False: + return (False, self.flash_burn_retry) + ret, data = self.flash_read_jedec_id_process() + if ret: + self._need_shake_hand = False + data = binascii.hexlify(data).decode('utf-8') + self.id2_valid_flag = data[6:] + read_id2 = data[0:6] + self.read_flash2_id = read_id2 + if self.cfg.has_option('FLASH2_CFG', 'flash2_para'): + flash2_para_file = os.path.join(app_path, self.cfg.get('FLASH2_CFG', 'flash2_para')) + self.flash_para_update(flash2_para_file, read_id2) + fp = open_file(flash2_para_file, 'rb') + para_data = bytearray(fp.read()) + fp.close() + para_data[0:1] = b'\x11' + fp = open_file(flash2_para_file, 'wb+') + fp.write(para_data) + fp.close() + else: + self.error_code_print('0030') + return ( + False, self.flash_burn_retry) + ret = self.flash_switch_bank_process(0) + self._need_shake_hand = False + if ret is False: + return (False, self.flash_burn_retry) + return (True, 'continue') + + def get_flash1_and_flash2(self, flash_file, address, size_current, i): + if self._flash1_size != 0: + if self._flash1_size < int(address[i], 16) + size_current: + if self._flash1_size > int(address[i], 16): + if self._flash2_select is False: + printf('%s file is overflow with flash1' % flash_file[i]) + flash1_bin, flash1_bin_len, flash2_bin, flash2_bin_len = self.flash_loader_cut_flash_bin(flash_file[i], int(address[i], 16), self._flash1_size) + return ( + flash1_bin, flash1_bin_len, flash2_bin, flash2_bin_len) + return ('', 0, '', 0) + + def set_clear_boot_status(self, shakehand=0): + self.clear_boot_status(shakehand) + + def get_new_bh_data(self, section, bh_data, fp): + if section == 'BOOTHEADER_GROUP0_CFG': + fp.write(bh_data[100:120]) + + def write_flash_data(self, file, start_addr, callback): + pass + + def get_flash_pin_from_bootinfo(self, chiptype, bootinfo): + sw_usage_data = bootinfo[22:24] + bootinfo[20:22] + bootinfo[18:20] + bootinfo[16:18] + sw_usage_data = int(sw_usage_data, 16) + return sw_usage_data >> 14 & 63 + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class BL628EflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl628, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def set_clear_boot_status(self, shakehand=0): + self.clear_boot_status(shakehand) + + def get_new_bh_data(self, section, bh_data, fp): + if section == 'BOOTHEADER_GROUP0_CFG': + fp.write(bh_data[100:124]) + + def write_flash_data(self, file, start_addr, callback): + pass + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class BL702EflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl702, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def set_load_function(self): + if self.chip_type == 'bl702': + self.load_function = 0 + + def set_decompress_write(self): + self.decompress_write = False + + def get_flash_pin(self): + return 255 + + def show_identify_fail(self): + printf('eflash loader identify flash fail!') + self.error_code_print('0043') + return False + + def reset_cpu(self, shakehand=0): + printf('CPU Reset') + if shakehand != 0: + printf(FLASH_ERASE_SHAKE_HAND) + if self.img_load_shake_hand() is False: + return False + cmd_id = hexstr_to_bytearray(self._com_cmds.get('reset')['cmd_id']) + ret, dmy = self.com_process_one_cmd('reset', cmd_id, bytearray(0)) + if ret.startswith('OK'): + return True + self.error_code_print('0004') + return False + + def run_reset_cpu(self): + if self.isp_mode_sign is True: + self.reset_cpu() + + def get_chip_id(self, bootinfo): + chip_id = bootinfo[32:34] + bootinfo[34:36] + bootinfo[36:38] + bootinfo[38:40] + bootinfo[40:42] + bootinfo[42:44] + bootinfo[44:46] + bootinfo[46:48] + return chip_id + + def get_mac_len(self): + return 8 + + def set_temp_timeout(self): + self.bflb_serial_object.set_timeout(self._default_time_out) + + def get_isp_sh_time(self): + return self._isp_shakehand_timeout + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class BL702LEflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl702l, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def reset_cpu(self, shakehand=0): + printf('CPU Reset') + if shakehand != 0: + printf(FLASH_ERASE_SHAKE_HAND) + if self.img_load_shake_hand() is False: + return False + cmd_id = hexstr_to_bytearray(self._com_cmds.get('reset')['cmd_id']) + ret, dmy = self.com_process_one_cmd('reset', cmd_id, bytearray(0)) + if ret.startswith('OK'): + return True + self.error_code_print('0004') + return False + + def run_reset_cpu(self): + if self.isp_mode_sign is True: + self.reset_cpu() + + def get_chip_id(self, bootinfo): + chip_id = bootinfo[32:34] + bootinfo[34:36] + bootinfo[36:38] + bootinfo[38:40] + bootinfo[40:42] + bootinfo[42:44] + bootinfo[44:46] + bootinfo[46:48] + return chip_id + + def get_new_bh_data(self, section, bh_data, fp): + if section == 'BOOTHEADER_CFG': + fp.write(bh_data[100:116]) + + def get_mac_len(self): + return 8 + + def set_temp_timeout(self): + self.bflb_serial_object.set_timeout(self._default_time_out) + + def get_isp_sh_time(self): + return self._isp_shakehand_timeout + + def get_flash_pin_from_bootinfo(self, chiptype, bootinfo): + dev_info_data = bootinfo[30:32] + bootinfo[28:30] + bootinfo[26:28] + bootinfo[24:26] + dev_info_data = int(dev_info_data, 16) + flash_cfg = dev_info_data >> 26 & 7 + sf_reverse = dev_info_data >> 29 & 1 + sf_swap_cfg = dev_info_data >> 22 & 3 + if flash_cfg == 0: + return 0 + if sf_reverse == 0: + return sf_swap_cfg + 1 + return sf_swap_cfg + 5 + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class BL808EflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is bl808, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def set_clear_boot_status(self, shakehand=0): + self.clear_boot_status(shakehand) + + def get_new_bh_data(self, section, bh_data, fp): + if section == 'BOOTHEADER_GROUP0_CFG': + fp.write(bh_data[100:128]) + + def write_flash_data(self, file, start_addr, callback): + fp = open_file(file, 'rb') + flash_data = bytearray(fp.read()) + fp.close() + flash_data_len = len(flash_data) + end_addr = start_addr + flash_data_len - 1 + if start_addr <= 4096: + if end_addr > 4096: + ret, flash_read_data = self.flash_read_main_process(4096, 4096, 0, None, callback) + if flash_read_data[0:4] == int_to_4bytearray_b(1112298054): + printf('RF para already write at flash 0x1000 addr, replace it.') + flash_data[4096:8192] = flash_read_data[0:4096] + fp = open_file(file, 'wb') + fp.write(flash_data) + fp.close() + + def is_conf_exist(self, flash_id): + cfg_dir = app_path + '/utils/flash/bl808/' + conf_name = self.get_suitable_conf_name(cfg_dir, flash_id) + if os.path.isfile(cfg_dir + conf_name) is False: + return False + return True + + def get_flash_pin_from_bootinfo(self, chiptype, bootinfo): + sw_usage_data = bootinfo[22:24] + bootinfo[20:22] + bootinfo[18:20] + bootinfo[16:18] + sw_usage_data = int(sw_usage_data, 16) + return sw_usage_data >> 14 & 31 + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) + + +class OtherEflashLoader(BaseEflashLoader): + __doc__ = '\n When chip is not bl602,bl702,bl702l,bl808,bl616.wb03, eflash Loader\n ' + + def __init__(self, chip_type, args, config, callback=None, macaddr_callback=None, create_simple_callback=None, create_img_callback=None, task_num=None): + super().__init__(chip_type, args, config, callback, macaddr_callback, create_simple_callback, create_img_callback, task_num) + self.chip_type = chip_type + + def run_step(self): + result, content = self.first_run_step_load_parameter() + if not result or content != 'continue': + return (result, content) + if self.isp_mode_sign is True: + self.load_function = 1 + result, content = self.second_run_step_shake_hand() + if not result or content != 'continue': + return (result, content) + result, content = self.third_run_step_read_mac_address() + if not result or content != 'continue': + return (result, content) + result, content = self.fourth_run_step_interact_chip() + if not result or content != 'continue': + return (result, content) + result, content = self.fifth_run_step_write_flash_and_check() + if not result or content != 'continue': + return (result, content) + result, content = self.sixth_run_step_write_efuse() + if not result or content != 'continue': + return (result, content) + result, content = self.seventh_run_step_erase() + if not result or content != 'continue': + return (result, content) + result, content = self.eighth_run_step_read() + if not result or content != 'continue': + return (result, content) + result, content = self.ninth_run_step_end() + return ( + result, content) +# okay decompiling ./libs/bflb_eflash_loader.pyc diff --git a/libs/bflb_interface_cklink.py b/libs/bflb_interface_cklink.py new file mode 100644 index 0000000..fd28ce6 --- /dev/null +++ b/libs/bflb_interface_cklink.py @@ -0,0 +1,337 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_interface_cklink.py +import os, time, binascii, subprocess, config as gol, cklink +from libs import bflb_utils +from libs.bflb_utils import app_path +dir_dll = os.path.join(app_path, 'utils/cklink') + +class BflbCKLinkPort(object): + + def __init__(self, vid=0, pid=0): + self._speed = 5000 + self._rx_timeout = 10000 + self._cklink_shake_hand_addr = '20000000' + self._cklink_data_addr = '20000004' + self._cklink_run_addr = '22010000' + self._cklink_reg_pc = 32 + self._inited = False + self._chiptype = 'bl808' + self._chipname = 'bl808' + self.vid = vid + self.pid = pid + self.link = gol.obj_cklink + + def if_init(self, device, sn, rate, chiptype='bl808', chipname='bl808'): + if self._inited is False: + dev = device.split('|') + vid = int(dev[0].replace('0x', ''), 16) + pid = int(dev[1].replace('0x', ''), 16) + serial = str(sn) + bflb_utils.printf('SN is ' + serial) + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._cklink_shake_hand_addr = sub_module.cklink_load_cfg.cklink_shake_hand_addr + self._cklink_data_addr = sub_module.cklink_load_cfg.cklink_data_addr + self._cklink_run_addr = sub_module.cklink_load_cfg.cklink_run_addr + self._cklink_vid = vid + self._cklink_pid = pid + self._speed = rate + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + if not self.link: + self.link = cklink.CKLink(dlldir=dir_dll, vid=(self._cklink_vid), pid=(self._cklink_pid), sn=serial, arch=2, cdi=0) + gol.obj_cklink = self.link + self.link.open() + if self.link.connected(): + self.link.reset(1) + return False + + def repeat_init(self, device, sn, rate, chiptype='bl808', chipname='bl808'): + if self._inited is False: + dev = device.split('|') + vid = int(dev[0].replace('0x', ''), 16) + pid = int(dev[1].replace('0x', ''), 16) + serial = str(sn) + bflb_utils.printf('SN is ' + serial) + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._cklink_shake_hand_addr = sub_module.cklink_load_cfg.cklink_shake_hand_addr + self._cklink_data_addr = sub_module.cklink_load_cfg.cklink_data_addr + self._cklink_run_addr = sub_module.cklink_load_cfg.cklink_run_addr + self._cklink_vid = vid + self._cklink_pid = pid + self._speed = rate + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + if not self.link: + self.link = cklink.CKLink(dlldir=dir_dll, vid=(self._cklink_vid), pid=(self._cklink_pid), sn=serial, arch=2, cdi=0) + gol.obj_cklink = self.link + self.link.open() + if self.link.connected(): + self.link.reset(1) + return False + + def if_close(self): + if self.link: + try: + try: + self.link.close() + except Exception as e: + try: + print(e) + finally: + e = None + del e + + finally: + self._inited = False + + def close(self): + if self.link: + try: + try: + self.link.close() + except Exception as e: + try: + print(e) + finally: + e = None + del e + + finally: + self._inited = False + + def if_clear_buf(self): + pass + + def clear_buf(self): + pass + + def if_set_rx_timeout(self, val): + self._rx_timeout = val * 1000 + + def set_timeout(self, val): + self._rx_timeout = val * 1000 + + def if_get_rate(self): + return self._speed + + def halt_cpu(self): + return self.link.halt() + + def resume_cpu(self): + return self.link.resume() + + def reset_cpu(self): + return self.link.reset(1) + + def set_pc_msp(self, pc, msp): + self.halt_cpu() + if self._chiptype == 'bl602' or self._chiptype == 'bl702' or self._chiptype == 'bl702l': + addr = int(self._cklink_run_addr, 16) + self.link.write_cpu_reg(self._cklink_reg_pc, addr) + + def if_raw_write(self, addr, data_send): + self.halt_cpu() + addr_int = int(addr, 16) + data_send = bytes(data_send) + self.link.write_memory(addr_int, data_send) + self.resume_cpu() + + def if_write(self, data_send): + self.if_raw_write(self._cklink_data_addr, data_send) + self.if_raw_write(self._cklink_shake_hand_addr, binascii.unhexlify('48524459')) + + def write(self, data_send): + self.if_raw_write(self._cklink_data_addr, data_send) + self.if_raw_write(self._cklink_shake_hand_addr, binascii.unhexlify('48524459')) + + def if_read(self, data_len): + start_time = time.time() * 1000 + while True: + self.halt_cpu() + ready = self.link.read_memory(int(self._cklink_shake_hand_addr, 16), 4) + if len(ready) >= 1: + ready = binascii.hexlify(ready).decode() + if ready == '5341434b': + self.resume_cpu() + break + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + else: + self.resume_cpu() + time.sleep(0.001) + + data = self.if_raw_read(self._cklink_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def read(self, data_len): + start_time = time.time() * 1000 + while True: + self.halt_cpu() + ready = self.link.read_memory(int(self._cklink_shake_hand_addr, 16), 4) + if len(ready) >= 1: + ready = binascii.hexlify(ready).decode() + if ready == '5341434b': + self.resume_cpu() + break + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + else: + self.resume_cpu() + time.sleep(0.001) + + data = self.if_raw_read(self._cklink_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def if_raw_read(self, addr, data_len): + return self.if_raw_read8(addr, data_len) + + def if_raw_read8(self, addr, data_len): + self.halt_cpu() + data = self.link.read_memory(int(addr, 16), data_len) + self.resume_cpu() + return bytearray(data) + + def if_shakehand(self, do_reset=False, reset_hold_time=100, shake_hand_delay=100, reset_revert=True, cutoff_time=0, shake_hand_retry=2, isp_timeout=0, boot_load=False): + self.if_write(bytearray(1)) + success, ack = self.if_read(2) + bflb_utils.printf(binascii.hexlify(ack)) + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + time.sleep(0.03) + return 'OK' + return 'FL' + + def if_deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def if_deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(16) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + def deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(16) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + +if __name__ == '__main__': + eflash_loader_t = BflbCKLinkPort() + eflash_loader_t.if_init('', 100, 'bl702') + bflb_utils.printf('read test') + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 16)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 16)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 16)) + bflb_utils.printf('write test') + data = bytearray([ + 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3, + 4]) + eflash_loader_t.if_raw_write('42020000', data) + bflb_utils.printf(eflash_loader_t.if_raw_read('42020000', 62)) +# okay decompiling ./libs/bflb_interface_cklink.pyc diff --git a/libs/bflb_interface_eflash_loader.py b/libs/bflb_interface_eflash_loader.py new file mode 100644 index 0000000..dd36fd8 --- /dev/null +++ b/libs/bflb_interface_eflash_loader.py @@ -0,0 +1,631 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_interface_eflash_loader.py +__doc__ = '\nCreated on 20220909\n\n@author: Dillon\n' +import os, sys, time, traceback, binascii +from libs.base import bflb_img_loader +from libs.bflb_utils import * +from libs.bflb_eflash_loader import * + +class InterfaceEflashLoader(object): + + def __init__(self): + self._temp_task_num = None + + def error_code_print(self, code): + set_error_code(code, self._temp_task_num) + printf('{"ErrorCode": "' + code + '","ErrorMsg":"' + eflash_loader_error_code[code] + '"}') + + def get_chip_type(self, interface, device, chip_type, callback): + if interface.lower() == 'uart' or interface == 'sdio': + boot_speed = 500000 + else: + if chip_type == 'bl606p': + return 'bl808' + return chip_type + _bflb_com_img_loader = bflb_img_loader.BflbImgLoader(device, boot_speed, boot_speed, (interface.lower()), callback=callback) + bflb_serial_object = _bflb_com_img_loader.bflb_serial_object + try: + ret, bootinfo = _bflb_com_img_loader.img_get_bootinfo(boot_speed, boot_speed, + callback=callback, + do_reset=True, + reset_hold_time=5, + shake_hand_delay=100, + reset_revert=False, + cutoff_time=100, + shake_hand_retry=2, + isp_mode_sign=False, + isp_timeout=0, + boot_load=True) + bootinfo = bootinfo.decode('utf-8') + bflb_serial_object.close() + if ret is False: + _bflb_com_img_loader = bflb_img_loader.BflbImgLoader(device, boot_speed, boot_speed, (interface.lower()), 'bl808', callback=callback) + bflb_serial_object = _bflb_com_img_loader.bflb_serial_object + ret, bootinfo = _bflb_com_img_loader.img_get_bootinfo(boot_speed, boot_speed, + callback=callback, + do_reset=True, + reset_hold_time=5, + shake_hand_delay=100, + reset_revert=False, + cutoff_time=100, + shake_hand_retry=2, + isp_mode_sign=False, + isp_timeout=0, + boot_load=True) + bootinfo = bootinfo.decode('utf-8') + bflb_serial_object.close() + if ret is False: + self.error_code_print('0003') + return 'Error: Can not get chip type!!' + if '01000000' in bootinfo: + return 'bl602' + if '01000207' in bootinfo: + return 'bl702' + if '01001606' in bootinfo: + return 'bl616' + if '01000808' in bootinfo: + return 'bl808' + return 'Error: Can not get chip type!!' + except Exception as e: + try: + self.error_code_print('0003') + return 'Error: Can not get chip type!!' + finally: + e = None + del e + + def judge_result(self, temp_eflash_obj, result, args, start_time): + do_reset = True + reset_hold_time = 100 + shake_hand_delay = 100 + reset_revert = True + cutoff_time = 0 + shake_hand_retry = 2 + if result == 'repeat_burn': + temp_eflash_obj.close_serial() + return 'repeat_burn' + if temp_eflash_obj.cpu_reset is True: + printf('Reset cpu') + temp_eflash_obj.base_reset_cpu() + if temp_eflash_obj.retry_delay_after_cpu_reset > 0: + printf('delay for uart timeout: ', temp_eflash_obj.retry_delay_after_cpu_reset) + time.sleep(temp_eflash_obj.retry_delay_after_cpu_reset) + if result is True: + printf('All time cost(ms): ', time.time() * 1000 - start_time) + time.sleep(0.1) + if not args.none: + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + printf('close interface') + printf('[All Success]') + local_log_save('log', temp_eflash_obj.input_macaddr) + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + return True + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + return False + + def run(self, act, config, callback=None): + interface = config['param']['interface_type'].lower() + error = None + flash_burn_retry = 1 + clear_global() + try: + try: + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + if not speed.isdigit(): + error = '{"ErrorCode":"FFFF","ErrorMsg":"BAUDRATE MUST BE DIGIT"}' + printf(error) + return error + chip_type = config['param']['chip_type'] + if act == 'download': + options = [ + '--write', '--flash'] + try: + if config['check_box']['efuse']: + options.extend(['--efuse']) + except: + pass + + parser = eflash_loader_parser_init() + args = parser.parse_args(options) + start_time = time.time() * 1000 + if 'Error' in chip_type: + error = '{"ErrorCode":"FFFF","ErrorMsg":"BFLB CAN NOT GET CHIP TYPE"}' + printf(error) + return error + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + while flash_burn_retry: + retry = -1 + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + else: + printf('Program Start') + result, content = temp_eflash_obj.run_step() + self._temp_task_num = temp_eflash_obj.task_num + temp_result = self.judge_result(temp_eflash_obj, result, args, start_time) + if temp_result: + return True + printf('Burn Retry') + flash_burn_retry -= 1 + + printf('Burn return with retry fail') + local_log_save('log', temp_eflash_obj.input_macaddr) + error = errorcode_msg(self._temp_task_num) + except Exception as e: + try: + traceback.print_exc(limit=10, file=(sys.stdout)) + error = str(e) + finally: + e = None + del e + + finally: + return error + + def program_read_id(self, config, callback=None): + flash_burn_retry = 1 + interface = config['param']['interface_type'] + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + chip_type = config['param']['chip_type'] + try: + if not (config['param']['speed_uart'].isdigit() and config['param']['speed_jlink'].isdigit()): + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BAUDRATE MUST BE DIGIT"}' + printf(ret) + return ( + False, ret) + set_error_code('FFFF') + start_time = time.time() * 1000 + eflash_loader_cfg_tmp = os.path.join(chip_path, chip_type, 'eflash_loader/eflash_loader_cfg.ini') + options = ['--none', '--flash', '-c', eflash_loader_cfg_tmp] + parser_eflash = eflash_loader_parser_init() + args = parser_eflash.parse_args(options) + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + result, content = temp_eflash_obj.run_step() + ret, data = temp_eflash_obj.flash_read_jedec_id_process() + temp_eflash_obj.object_status_clear() + self._temp_task_num = temp_eflash_obj.task_num + if ret: + data = binascii.hexlify(data).decode('utf-8') + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + return (ret, data) + except Exception as e: + try: + ret = str(e) + printf('error:' + ret) + return ( + False, ret) + finally: + e = None + del e + + def program_read_reg(self, config, callback=None): + flash_burn_retry = 1 + interface = config['param']['interface_type'] + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + chip_type = config['param']['chip_type'] + try: + if not (config['param']['speed_uart'].isdigit() and config['param']['speed_jlink'].isdigit()): + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BAUDRATE MUST BE DIGIT"}' + printf(ret) + return ( + False, ret) + set_error_code('FFFF') + start_time = time.time() * 1000 + eflash_loader_cfg_tmp = os.path.join(chip_path, chip_type, 'eflash_loader/eflash_loader_cfg.ini') + options = ['--none', '--flash', '-c', eflash_loader_cfg_tmp] + parser_eflash = eflash_loader_parser_init() + args = parser_eflash.parse_args(options) + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + result, content = temp_eflash_obj.run_step() + temp_eflash_obj.object_status_clear() + cmd = config['cmd'] + length = int(config['len']) + cmd_value = int(cmd, 16) + if cmd_value != 5: + if cmd_value != 53: + if cmd_value != 21: + temp_eflash_obj.close_serial() + ret = 'read register command value not recognize' + printf(ret) + return ( + False, ret) + if length > 3: + temp_eflash_obj.close_serial() + ret = 'read register len is too long' + printf(ret) + return ( + False, ret) + ret, data = temp_eflash_obj.flash_read_status_reg_process(cmd, length) + if ret: + data = binascii.hexlify(data).decode('utf-8') + if temp_eflash_obj.bflb_serial_object is not None: + temp_eflash_obj.close_serial() + self._temp_task_num = temp_eflash_obj.task_num + return ( + ret, data) + except Exception as e: + try: + ret = str(e) + printf('error:' + ret) + return ( + False, ret) + finally: + e = None + del e + + def program_write_reg(self, config, callback=None): + ret = None + flash_burn_retry = 1 + interface = config['param']['interface_type'] + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + chip_type = config['param']['chip_type'] + try: + if not device: + if interface.lower() == 'uart': + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BFLB INTERFACE HAS NO COM PORT"}' + printf(ret) + return ( + False, ret) + if not (config['param']['speed_uart'].isdigit() and config['param']['speed_jlink'].isdigit()): + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BAUDRATE MUST BE DIGIT"}' + printf(ret) + return ( + False, ret) + set_error_code('FFFF') + start_time = time.time() * 1000 + eflash_loader_cfg_tmp = os.path.join(chip_path, chip_type, 'eflash_loader/eflash_loader_cfg.ini') + options = ['--none', '--flash', '-c', eflash_loader_cfg_tmp] + parser_eflash = eflash_loader_parser_init() + args = parser_eflash.parse_args(options) + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + result, content = temp_eflash_obj.run_step() + temp_eflash_obj.object_status_clear() + cmd = config['cmd'] + length = int(config['len']) + val = config['val'] + cmd_value = int(cmd, 16) + if cmd_value != 1: + if cmd_value != 49: + if cmd_value != 17: + temp_eflash_obj.close_serial() + ret = 'write register command value not recognize' + printf(ret) + return ( + False, ret) + if length > 3: + temp_eflash_obj.close_serial() + ret = 'write register len is too long' + printf(ret) + return ( + False, ret) + ret, data = temp_eflash_obj.flash_write_status_reg_process(cmd, length, val) + temp_eflash_obj.close_serial() + self._temp_task_num = temp_eflash_obj.task_num + return ( + ret, data) + except Exception as e: + try: + ret = str(e) + printf('error:' + ret) + return ( + False, ret) + finally: + e = None + del e + + def read_flash_thread(self, config, callback=None): + ret = None + flash_burn_retry = 1 + interface = config['param']['interface_type'] + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + chip_type = config['param']['chip_type'] + try: + try: + if not device: + if interface.lower() == 'uart': + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BFLB INTERFACE HAS NO COM PORT"}' + printf(ret) + return ( + False, ret) + if not (config['param']['speed_uart'].isdigit() and config['param']['speed_jlink'].isdigit()): + ret = '{"ErrorCode":"FFFF","ErrorMsg":"BAUDRATE MUST BE DIGIT"}' + printf(ret) + return ( + False, ret) + set_error_code('FFFF') + start_time = time.time() * 1000 + eflash_loader_cfg_tmp = os.path.join(chip_path, chip_type, 'eflash_loader/eflash_loader_cfg.ini') + if verify_hex_num(config['start_addr'][2:]) is True: + if config['start_addr'][0:2] == '0x': + start = config['start_addr'][2:] + else: + printf('Error, start_addr is HEX data, must begin with 0x') + ret = 'start_addr is HEX data, must begin with 0x' + else: + printf('Error, Please check start_addr hex data') + ret = 'Please check start_addr hex data' + if verify_hex_num(config['end_addr'][2:]) is True: + if config['end_addr'][0:2] == '0x': + end = config['end_addr'][2:] + else: + printf('Error, end_addr is HEX data, must begin with 0x') + ret = 'end_addr is HEX data, must begin with 0x' + else: + printf('Error, Please check end_addr hex data') + ret = 'Please check end_addr hex data' + if int(start, 16) >= int(end, 16): + printf('Error, Start addr must less than end addr') + ret = 'Start addr must less than end addr' + if ret is not None: + return ret + printf('Save as flash.bin') + options = ['--read', '--flash', '--start=' + start, '--end=' + end, '--file=flash.bin', '-c', eflash_loader_cfg_tmp] + parser_eflash = eflash_loader_parser_init() + args = parser_eflash.parse_args(options) + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + result, content = temp_eflash_obj.run_step() + temp_eflash_obj.object_status_clear() + self._temp_task_num = temp_eflash_obj.task_num + temp_result = self.judge_result(temp_eflash_obj, result, args, start_time) + if temp_result: + return True + printf('Burn Retry') + flash_burn_retry -= 1 + printf('Burn return with retry fail') + local_log_save('log', temp_eflash_obj.input_macaddr) + ret = errorcode_msg(self._temp_task_num) + except Exception as e: + try: + ret = str(e) + finally: + e = None + del e + + finally: + return ret + + def erase_flash_thread(self, config, callback=None): + options = '' + start = '' + end = '' + ret = None + flash_burn_retry = 1 + interface = config['param']['interface_type'] + device = config['param']['comport_uart'] + if interface.lower() == 'uart': + speed = config['param']['speed_uart'] + else: + speed = config['param']['speed_jlink'] + chip_type = config['param']['chip_type'] + start_time = time.time() * 1000 + try: + try: + if verify_hex_num(config['start_addr'][2:]) is True: + if config['start_addr'][0:2] == '0x': + start = config['start_addr'][2:] + else: + printf('Error, start_addr is HEX data, must begin with 0x') + ret = 'start_addr is HEX data, must begin with 0x' + else: + if config['whole_chip'] is False: + printf('Error, Please check start_addr hex data') + ret = 'Please check start_addr hex data' + if verify_hex_num(config['end_addr'][2:]) is True: + if config['end_addr'][0:2] == '0x': + end = config['end_addr'][2:] + else: + printf('Error, end_addr is HEX data, must begin with 0x') + ret = 'end_addr is HEX data, must begin with 0x' + else: + if config['whole_chip'] is False: + printf('Error, Please check end_addr hex data') + ret = 'Please check end_addr hex data' + if config['whole_chip'] is False: + if int(start, 16) >= int(end, 16): + printf('Error, Start addr must less than end addr') + ret = 'Start addr must less than end addr' + if ret is not None: + return ret + eflash_loader_cfg_tmp = os.path.join(chip_path, chip_type, 'eflash_loader/eflash_loader_cfg.ini') + if config['whole_chip'] is True: + options = [ + '--erase','--flash','--end=0','-c',eflash_loader_cfg_tmp] + else: + options = [ + '--erase', '--flash', '--start=' + start, '--end=' + end, '-c', eflash_loader_cfg_tmp] + parser_eflash = eflash_loader_parser_init() + args = parser_eflash.parse_args(options) + config['param']['chip_xtal'] = 'auto' + if chip_type == 'bl602': + config['param']['chip_xtal'] = '40m' + temp_eflash_obj = BL602EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl702l': + config['param']['chip_xtal'] = '32m' + temp_eflash_obj = BL702LEflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl808' or chip_type == 'bl606p': + temp_eflash_obj = BL808EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl616' or chip_type == 'wb03': + temp_eflash_obj = BL616EflashLoader(chip_type, args, config, callback) + else: + if chip_type == 'bl628': + temp_eflash_obj = BL628EflashLoader(chip_type, args, config, callback) + else: + temp_eflash_obj = OtherEflashLoader(chip_type, args, config, callback) + result, content = temp_eflash_obj.run_step() + temp_eflash_obj.object_status_clear() + self._temp_task_num = temp_eflash_obj.task_num + temp_result = self.judge_result(temp_eflash_obj, result, args, start_time) + if temp_result: + return True + printf('Burn Retry') + flash_burn_retry -= 1 + printf('Burn return with retry fail') + local_log_save('log', temp_eflash_obj.input_macaddr) + ret = errorcode_msg(self._temp_task_num) + except Exception as e: + try: + ret = str(e) + finally: + e = None + del e + + finally: + return ret + + +if __name__ == '__main__': + act = 'download' + config_value_dict = { + 'interface_type': 'uart', + 'comport_uart': 'COM28', + 'speed_uart': '2000000', + 'chip_xtal': '40M', + 'verify': 'False', + 'ckb_erase_all': 'False'} + config = {'param': config_value_dict} + iel = InterfaceEflashLoader() + iel.run(act, config) +# okay decompiling ./libs/bflb_interface_eflash_loader.pyc diff --git a/libs/bflb_interface_jlink.py b/libs/bflb_interface_jlink.py new file mode 100644 index 0000000..580abb7 --- /dev/null +++ b/libs/bflb_interface_jlink.py @@ -0,0 +1,401 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_interface_jlink.py +import re, os, sys, time, binascii, traceback, struct, subprocess, pylink +from libs import bflb_utils +from libs.bflb_utils import app_path +python_version = struct.calcsize('P') * 8 +if python_version == 64: + path_dll = os.path.join(app_path, 'utils/jlink', 'JLink_x64.dll') +else: + path_dll = os.path.join(app_path, 'utils/jlink', 'JLinkARM.dll') + +class BflbJLinkPort(object): + + def __init__(self): + self._speed = 5000 + self._rx_timeout = 10000 + self._jlink_shake_hand_addr = '20000000' + self._jlink_data_addr = '20000004' + self._inited = False + self._chiptype = 'bl60x' + self._chipname = 'bl60x' + self._jlink_run_addr = '22010000' + + def if_init(self, device, rate, chiptype='bl60x', chipname='bl60x'): + if self._inited is False: + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._jlink_shake_hand_addr = sub_module.jlink_load_cfg.jlink_shake_hand_addr + self._jlink_data_addr = sub_module.jlink_load_cfg.jlink_data_addr + if sys.platform == 'win32': + obj_dll = pylink.Library(dllpath=path_dll) + self._jlink = pylink.JLink(lib=obj_dll) + self.jlink_path = os.path.join(app_path, 'utils/jlink', 'JLink.exe') + else: + self._jlink = pylink.JLink() + self.jlink_path = 'JLinkExe' + match = re.search('\\d{8,10}', device, re.I) + if match is not None: + bflb_utils.printf(device) + self._jlink.open(serial_no=(int(device))) + else: + self._jlink.open() + tif_set = sub_module.jlink_load_cfg.jlink_set_tif + self._jlink.set_tif(tif_set) + self._speed = rate + core_type = sub_module.jlink_load_cfg.jlink_core_type + self._jlink.connect(core_type, rate) + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + self._jlink_run_addr = sub_module.jlink_load_cfg.jlink_run_addr + self._device = device + + def repeat_init(self, device, rate, chiptype='bl60x', chipname='bl60x'): + if self._inited is False: + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._jlink_shake_hand_addr = sub_module.jlink_load_cfg.jlink_shake_hand_addr + self._jlink_data_addr = sub_module.jlink_load_cfg.jlink_data_addr + if sys.platform == 'win32': + obj_dll = pylink.Library(dllpath=path_dll) + self._jlink = pylink.JLink(lib=obj_dll) + self.jlink_path = os.path.join(app_path, 'utils/jlink', 'JLink.exe') + else: + self._jlink = pylink.JLink() + self.jlink_path = 'JLinkExe' + match = re.search('\\d{8,10}', device, re.I) + if match is not None: + bflb_utils.printf(device) + self._jlink.open(serial_no=(int(device))) + else: + self._jlink.open() + tif_set = sub_module.jlink_load_cfg.jlink_set_tif + self._jlink.set_tif(tif_set) + self._speed = rate + core_type = sub_module.jlink_load_cfg.jlink_core_type + self._jlink.connect(core_type, rate) + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + self._jlink_run_addr = sub_module.jlink_load_cfg.jlink_run_addr + self._device = device + + def if_clear_buf(self): + pass + + def clear_buf(self): + pass + + def if_set_rx_timeout(self, val): + self._rx_timeout = val * 1000 + + def set_timeout(self, val): + self._rx_timeout = val * 1000 + + def if_get_rate(self): + return self._speed + + def halt_cpu(self): + if self._jlink.halted() is False: + self._jlink.halt() + if self._jlink.halted(): + return True + bflb_utils.printf("couldn't halt cpu") + return False + + def reset_cpu(self, ms=0, halt=True): + if self._chiptype != 'bl60x': + self._jlink.set_reset_pin_low() + self._jlink.set_reset_pin_high() + return self._jlink.reset(ms, False) + + def set_pc_msp(self, pc, msp): + if self._jlink.halted() is False: + self._jlink.halt() + if self._jlink.halted(): + if self._chiptype == 'bl602' or self._chiptype == 'bl702' or self._chiptype == 'bl702l': + jlink_script = 'jlink.cmd' + fp = open(jlink_script, 'w+') + cmd = 'h\r\nSetPC ' + str(self._jlink_run_addr) + '\r\nexit' + bflb_utils.printf(cmd) + fp.write(cmd) + fp.close() + if self._device: + jlink_cmd = self.jlink_path + ' -device RISC-V -Speed {0} -SelectEmuBySN {1} -IF JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript jlink.cmd'.format(str(self._speed), str(self._device)) + else: + jlink_cmd = self.jlink_path + ' -device RISC-V -Speed {0} -IF JTAG -jtagconf -1,-1 -autoconnect 1 -CommanderScript jlink.cmd'.format(str(self._speed)) + bflb_utils.printf(jlink_cmd) + p = subprocess.Popen(jlink_cmd, shell=True, stdin=(subprocess.PIPE), stdout=(subprocess.PIPE), stderr=(subprocess.PIPE)) + out, err = p.communicate() + bflb_utils.printf(out, err) + os.remove(jlink_script) + else: + self._jlink.register_write(15, int(pc, 16)) + self._jlink.register_write(13, int(msp, 16)) + self._jlink.restart() + else: + bflb_utils.printf("couldn't halt cpu") + + def if_write(self, data_send): + self.if_raw_write(self._jlink_data_addr, data_send) + data_list = [] + data_list.append(int('59445248', 16)) + self._jlink.memory_write((int(self._jlink_shake_hand_addr, 16)), data_list, nbits=32) + + def write(self, data_send): + self.if_raw_write(self._jlink_data_addr, data_send) + data_list = [] + data_list.append(int('59445248', 16)) + self._jlink.memory_write((int(self._jlink_shake_hand_addr, 16)), data_list, nbits=32) + + def if_raw_write(self, addr, data_send): + addr_int = int(addr, 16) + len2 = len(data_send) % 4 + len1 = len(data_send) - len2 + if len1 != 0: + data_list = [] + for i in range(int(len1 / 4)): + data_list.append(data_send[4 * i] + (data_send[4 * i + 1] << 8) + (data_send[4 * i + 2] << 16) + (data_send[4 * i + 3] << 24)) + + self._jlink.memory_write(addr_int, data_list, nbits=32) + if len2 != 0: + data_list = [] + for i in range(len2): + data_list.append(data_send[len1 + i]) + + self._jlink.memory_write((addr_int + len1), data_list, nbits=8) + + def if_raw_write8(self, addr, data_send): + data_list = [] + for data in data_send: + data_list.append(data) + + self._jlink.memory_write((int(addr, 16)), data_list, nbits=8) + + def if_raw_write16(self, addr, data_send): + data_list = [] + for i in range(int(len(data_send) / 2)): + data_list.append(data_send[2 * i] + (data_send[2 * i + 1] << 8)) + + self._jlink.memory_write((int(addr, 16)), data_list, nbits=16) + + def if_raw_write32(self, addr, data_send): + data_list = [] + for i in range(int(len(data_send) / 4)): + data_list.append(data_send[4 * i] + (data_send[4 * i + 1] << 8) + (data_send[4 * i + 2] << 16) + (data_send[4 * i + 3] << 24)) + + self._jlink.memory_write((int(addr, 16)), data_list, nbits=32) + + def if_read(self, data_len): + start_time = time.time() * 1000 + while True: + ready = self._jlink.memory_read((int(self._jlink_shake_hand_addr, 16)), 1, nbits=32) + if len(ready) >= 1: + if ready[0] == int('4B434153', 16): + break + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + else: + time.sleep(0.001) + + data = self.if_raw_read(self._jlink_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def read(self, data_len): + start_time = time.time() * 1000 + while True: + ready = self._jlink.memory_read((int(self._jlink_shake_hand_addr, 16)), 1, nbits=32) + if len(ready) >= 1: + if ready[0] == int('4B434153', 16): + break + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + else: + time.sleep(0.001) + + data = self.if_raw_read(self._jlink_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def if_raw_read(self, addr, data_len): + addr_int = int(addr, 16) + if addr_int % 4 == 0: + len2 = data_len % 4 + len1 = data_len - len2 + data1 = bytearray(0) + data2 = bytearray(0) + if len1 != 0: + data1 = self._jlink.memory_read(addr_int, (int(len1 / 4)), nbits=32) + if len2 != 0: + data2 = self._jlink.memory_read((addr_int + len1), len2, nbits=8) + data = bytearray(0) + for tmp in data1: + data += bflb_utils.int_to_4bytearray_l(tmp) + + data += bytearray(data2) + return data + return self.if_raw_read8(addr, data_len) + + def if_raw_read8(self, addr, data_len): + data = self._jlink.memory_read((int(addr, 16)), data_len, nbits=8) + return bytearray(data) + + def if_raw_read16(self, addr, data_len): + raw_data = self._jlink.memory_read((int(addr, 16)), (data_len / 2), nbits=16) + data = bytearray(0) + for tmp in raw_data: + data += bflb_utils.int_to_2bytearray_l(tmp) + + return bytearray(data) + + def if_raw_read32(self, addr, data_len): + raw_data = self._jlink.memory_read((int(addr, 16)), (data_len / 4), nbits=32) + data = bytearray(0) + for tmp in raw_data: + data += bflb_utils.int_to_4bytearray_l(tmp) + + return bytearray(data) + + def if_shakehand(self, do_reset=False, reset_hold_time=100, shake_hand_delay=100, reset_revert=True, cutoff_time=0, shake_hand_retry=2, isp_timeout=0, boot_load=False): + self.if_write(bytearray(1)) + success, ack = self.if_read(2) + bflb_utils.printf(binascii.hexlify(ack)) + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + time.sleep(0.03) + return 'OK' + return 'FL' + + def if_close(self): + if self._jlink: + self._jlink.close() + self._inited = False + + def close(self): + if self._jlink: + self._jlink.close() + self._inited = False + + def if_deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def if_deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(4) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + def deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(4) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + +if __name__ == '__main__': + try: + eflash_loader_t = BflbJLinkPort() + eflash_loader_t.if_init('', 1000, 'bl602') + bflb_utils.printf('reset test') + res = eflash_loader_t.reset_cpu(0, False) + bflb_utils.printf(res) + except Exception as e: + try: + NUM_ERR = 5 + bflb_utils.printf(e) + traceback.print_exc(limit=NUM_ERR, file=(sys.stdout)) + finally: + e = None + del e +# okay decompiling ./libs/bflb_interface_jlink.pyc diff --git a/libs/bflb_interface_openocd.py b/libs/bflb_interface_openocd.py new file mode 100644 index 0000000..8ae351d --- /dev/null +++ b/libs/bflb_interface_openocd.py @@ -0,0 +1,425 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: libs/bflb_interface_openocd.py +import os, time, binascii, subprocess, threading, telnetlib, serial +from libs import bflb_utils +from libs.bflb_utils import app_path +openocd_path = os.path.join(app_path, 'utils/openocd', 'openocd.exe') + +class ThreadOpenocdServer(threading.Thread): + + def __init__(self, chiptype='bl602', device='rv_dbg_plus', serial=None): + threading.Thread.__init__(self) + self.timeToQuit = threading.Event() + self.timeToQuit.clear() + self._chiptype = chiptype + self._device = device + self._serial = serial + bflb_utils.printf('SN is ' + str(self._serial)) + + def stop(self): + self.timeToQuit.set() + + def run(self): + cmd = '' + if self._serial: + cmd_ftdi_serial = ' -c "ftdi_serial \\"' + self._serial + '\\""' + else: + cmd_ftdi_serial = '' + if self._device == 'rv_dbg_plus': + if self._chiptype == 'bl602': + cmd = openocd_path + ' -f ' + app_path + '/utils/openocd/if_rv_dbg_plus.cfg ' + cmd_ftdi_serial + ' -f ' + app_path + '/utils/openocd/tgt_602.cfg' + else: + cmd = openocd_path + ' -f ' + app_path + '/utils/openocd/if_rv_dbg_plus.cfg' + cmd_ftdi_serial + ' -f ' + app_path + '/utils/openocd/tgt_702.cfg' + else: + if self._device == 'ft2232hl': + if self._chiptype == 'bl602': + cmd = openocd_path + ' -f ' + app_path + '/utils/openocd/if_bflb_dbg.cfg' + cmd_ftdi_serial + ' -f ' + app_path + '/utils/openocd/tgt_602.cfg' + else: + cmd = openocd_path + ' -f ' + app_path + '/utils/openocd/if_bflb_dbg.cfg' + cmd_ftdi_serial + ' -f ' + app_path + '/utils/openocd/tgt_702.cfg' + else: + cmd = openocd_path + ' -f ' + app_path + '/utils/openocd/openocd-usb-sipeed.cfg ' + cmd_ftdi_serial + bflb_utils.printf(cmd) + p = subprocess.Popen(cmd, shell=True, stdin=(subprocess.PIPE), stdout=(subprocess.PIPE), stderr=(subprocess.PIPE)) + out, err = p.communicate() + bflb_utils.printf(out) + + +class BflbOpenocdPort(object): + + def __init__(self): + self._speed = 5000 + self._rx_timeout = 10000 + self._openocd_shake_hand_addr = '20000000' + self._openocd_data_addr = '20000004' + self._inited = False + self._chiptype = 'bl60x' + self._chipname = 'bl60x' + self._openocd_run_addr = '22010000' + self.tn = telnetlib.Telnet() + + def if_init(self, device, sn, rate, chiptype='bl60x', chipname='bl60x'): + if self._inited is False: + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._openocd_shake_hand_addr = sub_module.openocd_load_cfg.openocd_shake_hand_addr + self._openocd_data_addr = sub_module.openocd_load_cfg.openocd_data_addr + self._openocd_run_addr = sub_module.openocd_load_cfg.openocd_run_addr + self._speed = rate + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + if sn: + serial = 'FactoryAIOT Prog ' + str(sn) + else: + serial = None + self._openocd_th = None + self._openocd_th = ThreadOpenocdServer(chiptype, device, serial) + self._openocd_th.setDaemon(True) + self._openocd_th.start() + try: + self.tn.open('127.0.0.1', port=4444, timeout=10) + self.tn.write(('adapter speed ' + str(rate)).encode('ascii') + b'\n') + self.tn.write('WaitCmd\n'.encode('ascii')) + self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + except Exception: + bflb_utils.printf('Failed to connect openocd server') + bflb_utils.set_error_code('0009') + self.if_close() + + return False + + def repeat_init(self, device, sn, rate, chiptype='bl60x', chipname='bl60x'): + if self._inited is False: + sub_module = __import__(('libs.base.' + chiptype), fromlist=[chiptype]) + self._openocd_shake_hand_addr = sub_module.openocd_load_cfg.openocd_shake_hand_addr + self._openocd_data_addr = sub_module.openocd_load_cfg.openocd_data_addr + self._openocd_run_addr = sub_module.openocd_load_cfg.openocd_run_addr + self._speed = rate + self._inited = True + self._chiptype = chiptype + self._chipname = chipname + if sn: + serial = 'FactoryAIOT Prog ' + str(sn) + else: + serial = None + self._openocd_th = None + self._openocd_th = ThreadOpenocdServer(chiptype, device, serial) + self._openocd_th.setDaemon(True) + self._openocd_th.start() + try: + self.tn.open('127.0.0.1', port=4444, timeout=10) + self.tn.write(('adapter speed ' + str(rate)).encode('ascii') + b'\n') + self.tn.write('WaitCmd\n'.encode('ascii')) + self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + except Exception: + bflb_utils.printf('Failed to connect openocd server') + bflb_utils.set_error_code('0009') + self.if_close() + + return False + + def if_clear_buf(self): + pass + + def clear_buf(self): + pass + + def if_set_rx_timeout(self, val): + self._rx_timeout = val * 1000 + + def set_timeout(self, val): + self._rx_timeout = val * 1000 + + def if_get_rate(self): + return self._speed + + def halt_cpu(self): + self.tn.write('halt'.encode('ascii') + b'\n') + return True + + def reset_cpu(self, ms=0, halt=True): + if halt: + self.halt_cpu() + self.tn.write('reset'.encode('ascii') + b'\n') + + def set_pc_msp(self, pc, msp): + self.halt_cpu() + if self._chiptype == 'bl602' or self._chiptype == 'bl702' or self._chiptype == 'bl702l': + self.tn.write(('reg pc 0x' + self._openocd_run_addr).encode('ascii') + b'\n') + self.tn.write('resume'.encode('ascii') + b'\n') + + def if_raw_write(self, addr, data_send): + addr_int = int(addr, 16) + if len(data_send) > 32: + fp = open('openocd_load_data.bin', 'wb+') + fp.write(data_send) + fp.close() + self.tn.write(('load_image openocd_load_data.bin ' + hex(addr_int)).encode('ascii') + b'\n') + else: + for data in data_send: + self.tn.write(('mwb ' + hex(addr_int) + ' ' + hex(data)).encode('ascii') + b'\n') + addr_int += 1 + + def if_write(self, data_send): + self.if_raw_write(self._openocd_data_addr, data_send) + self.if_raw_write(self._openocd_shake_hand_addr, bytearray.fromhex('48524459')) + + def write(self, data_send): + self.if_raw_write(self._openocd_data_addr, data_send) + self.if_raw_write(self._openocd_shake_hand_addr, bytearray.fromhex('48524459')) + + def read_data_parse(self, buff, aligned): + strdata = buff.decode().strip() + data = bytearray(0) + index = strdata.find(': ') + if aligned is True: + lstr = strdata[index + 2:strdata.find('WaitCmd') - 6].split('0x') + for l in lstr: + ldata = [] + if l.find(': ') != -1: + ldata = l[9:].split(' ') + else: + ldata = l.split(' ') + for d in ldata: + if len(d) != 8: + continue + else: + hexstr = d[6:8] + d[4:6] + d[2:4] + d[0:2] + data += bflb_utils.hexstr_to_bytearray(hexstr) + + else: + data += bflb_utils.hexstr_to_bytearray(strdata[index + 2:strdata.find('WaitCmd') - 6].replace(' ', '')) + return data + + def if_addr_unaligned_read(self, addr, data_len): + addr_int = int(addr, 16) + data = bytearray(0) + dummy = self.tn.read_very_eager().decode('utf-8') + self.tn.write(('mdb ' + hex(addr_int) + ' ' + hex(data_len) + '\n').encode('ascii')) + self.tn.write('WaitCmd\n'.encode('ascii')) + ret = self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + data += self.read_data_parse(ret, False) + return data + + def if_addr_aligned_read(self, addr, data_len): + addr_int = int(addr, 16) + leftlen = data_len + data = bytearray(0) + dummy = self.tn.read_very_eager().decode('utf-8') + self.tn.write(('mdw ' + hex(addr_int) + ' ' + hex(data_len // 4) + '\n').encode('ascii')) + self.tn.write('WaitCmd\n'.encode('ascii')) + ret = self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + data += self.read_data_parse(ret, True) + addr_int = addr_int + data_len // 4 * 4 + leftlen = data_len - data_len // 4 * 4 + if leftlen != 0: + data += self.if_addr_unaligned_read(hex(addr_int)[2:], leftlen) + return data + + def if_raw_read(self, addr, data_len): + data = bytearray(0) + if data_len <= 4: + return self.if_addr_unaligned_read(addr, data_len) + addr_int = int(addr, 16) + pre_read_len = 4 - addr_int % 4 + if pre_read_len != 0: + data += self.if_addr_unaligned_read(addr, pre_read_len) + data += self.if_addr_aligned_read(hex(addr_int + pre_read_len), data_len - pre_read_len) + return data[:data_len] + + def if_read(self, data_len): + start_time = time.time() * 1000 + while True: + ready = self.if_raw_read(self._openocd_shake_hand_addr, 16) + if ready[0:4] == bytearray([83, 65, 67, 75]): + break + else: + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + time.sleep(0.001) + + data = self.if_raw_read(self._openocd_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def read(self, data_len): + start_time = time.time() * 1000 + while True: + ready = self.if_raw_read(self._openocd_shake_hand_addr, 16) + if ready[0:4] == bytearray([83, 65, 67, 75]): + break + else: + elapsed = time.time() * 1000 - start_time + if elapsed >= self._rx_timeout: + return (0, 'waiting response time out'.encode('utf-8')) + time.sleep(0.001) + + data = self.if_raw_read(self._openocd_data_addr, data_len) + if len(data) != data_len: + return (0, data) + return (1, data) + + def if_clear_buff(self): + self.tn.write('WaitCmd\n'.encode('ascii')) + self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + + def clear_buff(self): + self.tn.write('WaitCmd\n'.encode('ascii')) + self.tn.read_until(('"WaitCmd"'.encode('ascii')), timeout=10) + + def if_shakehand(self, do_reset=False, reset_hold_time=100, shake_hand_delay=100, reset_revert=True, cutoff_time=0, shake_hand_retry=2, isp_timeout=0, boot_load=False): + self.if_clear_buff() + self.if_write(bytearray(1)) + success, ack = self.if_read(2) + bflb_utils.printf(binascii.hexlify(ack)) + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + time.sleep(0.03) + return 'OK' + return 'FL' + + def if_close(self): + if self.tn.get_socket(): + self.tn.write('shutdown\n'.encode('ascii')) + time.sleep(0.05) + self.tn.close() + if self._openocd_th: + self._openocd_th.stop() + self._inited = False + + def close(self): + if self.tn.get_socket(): + self.tn.write('shutdown\n'.encode('ascii')) + time.sleep(0.05) + self.tn.close() + if self._openocd_th: + self._openocd_th.stop() + self._inited = False + + def if_deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def deal_ack(self): + success, ack = self.if_read(2) + if success == 0: + bflb_utils.printf('ack:' + str(binascii.hexlify(ack))) + return ack.decode('utf-8') + if ack.find(b'O') != -1 or ack.find(b'K') != -1: + return 'OK' + if ack.find(b'P') != -1 or ack.find(b'D') != -1: + return 'PD' + success, err_code = self.if_read(4) + if success == 0: + bflb_utils.printf('err_code:' + str(binascii.hexlify(err_code))) + return 'FL' + err_code_str = str(binascii.hexlify(err_code[3:4] + err_code[2:3]).decode('utf-8')) + ack = 'FL' + try: + ret = ack + err_code_str + '(' + bflb_utils.get_bflb_error_code(err_code_str) + ')' + except Exception: + ret = ack + err_code_str + ' unknown' + + bflb_utils.printf(ret) + return ret + + def if_deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(4) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + def deal_response(self): + ack = self.if_deal_ack() + if ack == 'OK': + success, len_bytes = self.if_read(4) + if success == 0: + bflb_utils.printf('Get length error') + bflb_utils.printf(binascii.hexlify(len_bytes)) + return ( + 'Get length error', len_bytes) + tmp = bflb_utils.bytearray_reverse(len_bytes[2:4]) + data_len = bflb_utils.bytearray_to_int(tmp) + success, data_bytes = self.if_read(data_len + 4) + if success == 0: + bflb_utils.printf('Read data error') + return ( + 'Read data error', data_bytes) + data_bytes = data_bytes[4:] + if len(data_bytes) != data_len: + bflb_utils.printf('Not get excepted length') + return ( + 'Not get excepted length', data_bytes) + return (ack, data_bytes) + bflb_utils.printf('Not ack OK') + bflb_utils.printf(ack) + return ( + ack, None) + + +if __name__ == '__main__': + eflash_loader_t = BflbOpenocdPort() + eflash_loader_t.if_init('', 100, 'bl60x') + bflb_utils.printf('read test') + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000000', 16)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000001', 16)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 2)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 4)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 10)) + bflb_utils.printf(eflash_loader_t.if_raw_read('21000002', 16)) + bflb_utils.printf('write test') + data = bytearray([ + 1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3, + 4]) + eflash_loader_t.if_raw_write('42020000', data) + bflb_utils.printf(eflash_loader_t.if_raw_read('42020000', 62)) +# okay decompiling ./libs/bflb_interface_openocd.pyc diff --git a/libs/bflb_utils.py b/libs/bflb_utils.py new file mode 100644 index 0000000..da972e6 --- /dev/null +++ b/libs/bflb_utils.py @@ -0,0 +1,476 @@ +import os +import sys +import re +import time +import shutil +import binascii +import struct +import random +import socket +import threading +import datetime +import hashlib +import argparse +import traceback +import platform +import codecs +from glob import glob +import pylink +from serial import Serial +from Crypto.Util import Counter +from Crypto.Cipher import AES +try: + from PySide2 import QtCore + qt_sign = True +except ImportError: + qt_sign = False +try: + from serial.tools.list_ports import comports +except ImportError: + pass +if getattr(sys, 'frozen', False): + app_path = os.path.dirname(sys.executable) +else: + app_path = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +chip_path = os.path.join(app_path, 'chips') +python_version = struct.calcsize('P')*8 +if python_version == 64: + path_dll = os.path.join(app_path, 'utils/jlink', 'JLink_x64.dll') +else: + path_dll = os.path.join(app_path, 'utils/jlink', 'JLinkARM.dll') +try: + import changeconf as cgc + conf_sign = True +except ImportError: + cgc = None + conf_sign = False +PY2 = sys.version_info[0] == 2 +udp_clinet_dict = {} +udp_send_log = False +udp_log_local_echo = False +udp_socket_server = None +error_code_num = 'FFFF' +error_code_num_task = ['FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF', 'FFFF'] +local_log_en = True +local_log_data = '' +if conf_sign: + bflb_error_code = {'0000': 'SUCCESS', '0001': 'FLASH_INIT_ERROR', '0002': 'FLASH_ERASE_PARA_ERROR', '0003': 'FLASH_ERASE_ERROR', '0004': 'FLASH_WRITE_PARA_ERROR', '0005': 'FLASH_WRITE_ADDR_ERROR', '0006': 'FLASH_WRITE_ERROR', '0007': 'FLASH_BOOT_PARA_ERROR', '0008': 'FLASH_SET_PARA_ERROR', '0009': 'FLASH_READ_STATUS_REG_ERROR', '000a': 'FLASH_WRITE_STATUS_REG_ERROR', '000b': 'FLASH_DECOMPRESS_WRITE_ERROR', '000c': 'FLASH_WRITE_XZ_ERROR', '000d': 'FLASH_SWITCH_BANK_ERROR', '0101': 'CMD_ID_ERROR', '0102': 'CMD_LEN_ERROR', '0103': 'CMD_CRC_ERROR', '0104': 'CMD_SEQ_ERROR', '0201': 'IMG_BOOTHEADER_LEN_ERROR', '0202': 'IMG_BOOTHEADER_NOT_LOAD_ERROR', '0203': 'IMG_BOOTHEADER_MAGIC_ERROR', '0204': 'IMG_BOOTHEADER_CRC_ERROR', '0205': 'IMG_BOOTHEADER_ENCRYPT_NOTFIT', '0206': 'IMG_BOOTHEADER_SIGN_NOTFIT', '0207': 'IMG_SEGMENT_CNT_ERROR', '0208': 'IMG_AES_IV_LEN_ERROR', '0209': 'IMG_AES_IV_CRC_ERROR', '020a': 'IMG_PK_LEN_ERROR', '020b': 'IMG_PK_CRC_ERROR', '020c': 'IMG_PK_HASH_ERROR', '020d': 'IMG_SIGNATURE_LEN_ERROR', '020e': 'IMG_SIGNATURE_CRC_ERROR', '020f': 'IMG_SECTIONHEADER_LEN_ERROR', '0210': 'IMG_SECTIONHEADER_CRC_ERROR', '0211': 'IMG_SECTIONHEADER_DST_ERROR', '0212': 'IMG_SECTIONDATA_LEN_ERROR', '0213': 'IMG_SECTIONDATA_DEC_ERROR', '0214': 'IMG_SECTIONDATA_TLEN_ERROR', '0215': 'IMG_SECTIONDATA_CRC_ERROR', '0216': 'IMG_HALFBAKED_ERROR', '0217': 'IMG_HASH_ERROR', '0218': 'IMG_SIGN_PARSE_ERROR', '0219': 'IMG_SIGN_ERROR', '021a': 'IMG_DEC_ERROR', '021b': 'IMG_ALL_INVALID_ERROR', '0301': 'IF_RATE_LEN_ERROR', '0302': 'IF_RATE_PARA_ERROR', '0303': 'IF_PASSWORDERROR', '0304': 'IF_PASSWORDCLOSE', '0401': 'EFUSE_WRITE_PARA_ERROR', '0402': 'EFUSE_WRITE_ADDR_ERROR', '0403': 'EFUSE_WRITE_ERROR', '0404': 'EFUSE_READ_PARA_ERROR', '0405': 'EFUSE_READ_ADDR_ERROR', '0406': 'EFUSE_READ_ERROR', '0407': 'EFUSE_READ_MAC_ERROR', '0408': 'EFUSE_WRITE_MAC_ERROR', '0501': 'MEMORY_WRITE_PARA_ERROR', '0502': 'MEMORY_WRITE_ADDR_ERROR', '0503': 'MEMORY_WRITE_ERROR', '0504': 'MEMORY_READ_PARA_ERROR', '0505': 'MEMORY_READ_ADDR_ERROR', '0506': 'MEMORY_READ_ERROR', '0508': 'REG_WRITE_PARA_ERROR', '0509': 'REG_WRITE_ADDR_ERROR', '050a': 'REG_WRITE_ERROR', '050b': 'REG_READ_PARA_ERROR', '050c': 'REG_READ_ADDR_ERROR', '050d': 'REG_READ_ERROR', '0601': 'ECDH_PARA_ERROR', '0602': 'ECDH_PRIVATE_KEY_ERROR', '0603': 'ECDH_SHARED_KEY_ERROR', '0604': 'ECDH_RANDOM_VAL_ERROR', '0605': 'ECDH_DECRYPT_ERROR', '0606': 'ECDH_ENCRYPT_ERROR', 'fffc': 'PLL_ERROR', 'fffd': 'INVASION_ERROR', 'fffe': 'POLLING', 'ffff': 'FAIL'} + eflash_loader_error_code = {'0000': 'SUCCESS', '0001': 'EFLASH LOADER SHAKEHAND FAIL', '0002': 'OPTION SET UNSUPPORTED', '0003': 'LOAD HELP BIN FAIL', '0004': 'RESET CPU FAIL', '0005': 'BURN RETRY FAIL', '0006': 'LOG READ FAIL', '0009': 'CONNECT OPENOCD SERVER FAIL', '000A': 'REPEAT BURN', '000B': 'CONFIG FILE NOT FOUND', '000C': 'SET CLOCK PLL FAIL', '000D': 'SET OPT FINISH FAIL', '000E': 'IMPORT PACKET FAIL', '0020': 'EFUSE READ FAIL', '0021': 'EFUSE WRITE FAIL', '0022': 'EFUSE COMPARE FAIL', '0023': 'EFUSE READ MAC ADDR FAIL', '0024': 'EFUSE WRITE MAC ADDR FAIL', '0025': 'EFUSE MAC ADDR CRC FAIL', '0030': 'READ FLASH JEDEC ID FAIL', '0031': 'READ FLASH STATUS REGISTER FAIL', '0032': 'WRITE FLASH STATUS REGISTER FAIL', '0033': 'FLASH CHIP ERASE FAIL', '0034': 'FLASH ERASE FAIL', '0035': 'FLASH READ FAIL', '0036': 'FLASH WRITE FAIL', '0037': 'FLASH WRITE CHECK FAIL', '0038': 'FLASH READ SHA FAIL', '0039': 'FLASH XIP MODE ENTER FAIL', '003A': 'FLASH XIP MODE EXIT FAIL', '003B': 'FLASH SET PARA FAIL', '003C': 'FLASH LOAD FIRMWARE BIN FAIL', '003D': 'FLASH MATCH TYPE FAIL', '003E': 'FLASH LOAD VERIFY FAIL', '003F': 'FLASH BOOT FAIL', '0040': 'FLASH CFG NOT FIT WITH BOOTHEADER', '0041': 'FLASH LOAD ROMFS FAIL', '0042': 'FLASH SWITCH BANK FAIL', '0043': 'FLASH IDENTIFY FAIL', '0044': 'FLASH CHIPERASE CONFLICT WITH SKIP MODE', '0050': 'IMG LOAD SHAKEHAND FAIL', '0051': 'IMG LOAD BOOT CHECK FAIL', '0060': 'IMG CREATE FAIL', '0061': 'IMG FILE NOT SET', '0062': 'IMG ADDR NOT SET', '0063': 'BOOTINFO ADDR NOT SET', '0064': 'AES KEY NOT SET', '0065': 'AES IV NOT SET', '0066': 'PUBLIC KEY NOT SET', '0067': 'PRIVATE KEY NOT SET', '0068': 'RAW DATA NOT NEED CREATE', '0069': 'FLASH ID NOT SUPPORT', '0070': 'FLASH ID NOT FOUND', '0071': 'ENCRYPT KEY LEN ERROR', '0072': 'AES IV LEN ERROR', '0073': 'AES IV SHOULD END WITH 00000000', '0074': 'IMG TYPE NOT FIT', '0075': 'IMG CREATE ENTER EXCEPT', '0076': 'PT NOT SET', '0077': 'AES KEY DATA OR LEN ERROR', '0078': 'AES IV DATA OR LEN ERROR', '0079': 'FACTORY IMG NOT FOUND', '007A': 'GENERATE ROMFS FAIL', '007B': 'PT PARCEL IS NULL', '007C': 'PT TABLE NOT SET', '007D': 'BOOT2 BIN NOT SET', '007E': 'FW BIN NOT SET', '007F': 'MEDIA NOT SET', '0080': 'ROMFS NOT SET', '0081': 'MFG BIN NOT SET', '0082': 'PT CHECK FAIL', '0083': 'D0 FW BIN NOT SET', '0084': 'IMTB BIN NOT SET', '0085': 'IMG LOADER BIN NOT SET', '0086': 'SBI BIN NOT SET', '0087': 'KERNEL BIN NOT SET', '0088': 'ROOTFS BIN NOT SET', '0089': 'KV BIN NOT SET', '0090': 'YOCBOOT BIN NOT SET', '0091': 'DTB BIN NOT SET', 'FFFF': 'BURN RETRY FAIL'} +else: + bflb_error_code = {'0000': 'BFLB_SUCCESS', '0001': 'BFLB_FLASH_INIT_ERROR', '0002': 'BFLB_FLASH_ERASE_PARA_ERROR', '0003': 'BFLB_FLASH_ERASE_ERROR', '0004': 'BFLB_FLASH_WRITE_PARA_ERROR', '0005': 'BFLB_FLASH_WRITE_ADDR_ERROR', '0006': 'BFLB_FLASH_WRITE_ERROR', '0007': 'BFLB_FLASH_BOOT_PARA_ERROR', '0008': 'BFLB_FLASH_SET_PARA_ERROR', '0009': 'BFLB_FLASH_READ_STATUS_REG_ERROR', '000a': 'BFLB_FLASH_WRITE_STATUS_REG_ERROR', '000b': 'BFLB_FLASH_DECOMPRESS_WRITE_ERROR', '000c': 'BFLB_FLASH_WRITE_XZ_ERROR', '000d': 'BFLB_FLASH_SWITCH_BANK_ERROR', '0101': 'BFLB_CMD_ID_ERROR', '0102': 'BFLB_CMD_LEN_ERROR', '0103': 'BFLB_CMD_CRC_ERROR', '0104': 'BFLB_CMD_SEQ_ERROR', '0201': 'BFLB_IMG_BOOTHEADER_LEN_ERROR', '0202': 'BFLB_IMG_BOOTHEADER_NOT_LOAD_ERROR', '0203': 'BFLB_IMG_BOOTHEADER_MAGIC_ERROR', '0204': 'BFLB_IMG_BOOTHEADER_CRC_ERROR', '0205': 'BFLB_IMG_BOOTHEADER_ENCRYPT_NOTFIT', '0206': 'BFLB_IMG_BOOTHEADER_SIGN_NOTFIT', '0207': 'BFLB_IMG_SEGMENT_CNT_ERROR', '0208': 'BFLB_IMG_AES_IV_LEN_ERROR', '0209': 'BFLB_IMG_AES_IV_CRC_ERROR', '020a': 'BFLB_IMG_PK_LEN_ERROR', '020b': 'BFLB_IMG_PK_CRC_ERROR', '020c': 'BFLB_IMG_PK_HASH_ERROR', '020d': 'BFLB_IMG_SIGNATURE_LEN_ERROR', '020e': 'BFLB_IMG_SIGNATURE_CRC_ERROR', '020f': 'BFLB_IMG_SECTIONHEADER_LEN_ERROR', '0210': 'BFLB_IMG_SECTIONHEADER_CRC_ERROR', '0211': 'BFLB_IMG_SECTIONHEADER_DST_ERROR', '0212': 'BFLB_IMG_SECTIONDATA_LEN_ERROR', '0213': 'BFLB_IMG_SECTIONDATA_DEC_ERROR', '0214': 'BFLB_IMG_SECTIONDATA_TLEN_ERROR', '0215': 'BFLB_IMG_SECTIONDATA_CRC_ERROR', '0216': 'BFLB_IMG_HALFBAKED_ERROR', '0217': 'BFLB_IMG_HASH_ERROR', '0218': 'BFLB_IMG_SIGN_PARSE_ERROR', '0219': 'BFLB_IMG_SIGN_ERROR', '021a': 'BFLB_IMG_DEC_ERROR', '021b': 'BFLB_IMG_ALL_INVALID_ERROR', '0301': 'BFLB_IF_RATE_LEN_ERROR', '0302': 'BFLB_IF_RATE_PARA_ERROR', '0303': 'BFLB_IF_PASSWORDERROR', '0304': 'BFLB_IF_PASSWORDCLOSE', '0401': 'BFLB_EFUSE_WRITE_PARA_ERROR', '0402': 'BFLB_EFUSE_WRITE_ADDR_ERROR', '0403': 'BFLB_EFUSE_WRITE_ERROR', '0404': 'BFLB_EFUSE_READ_PARA_ERROR', '0405': 'BFLB_EFUSE_READ_ADDR_ERROR', '0406': 'BFLB_EFUSE_READ_ERROR', '0407': 'BFLB_EFUSE_READ_MAC_ERROR', '0408': 'BFLB_EFUSE_WRITE_MAC_ERROR', '0501': 'BFLB_MEMORY_WRITE_PARA_ERROR', '0502': 'BFLB_MEMORY_WRITE_ADDR_ERROR', '0503': 'BFLB_MEMORY_WRITE_ERROR', '0504': 'BFLB_MEMORY_READ_PARA_ERROR', '0505': 'BFLB_MEMORY_READ_ADDR_ERROR', '0506': 'BFLB_MEMORY_READ_ERROR', '0508': 'BFLB_REG_WRITE_PARA_ERROR', '0509': 'BFLB_REG_WRITE_ADDR_ERROR', '050a': 'BFLB_REG_WRITE_ERROR', '050b': 'BFLB_REG_READ_PARA_ERROR', '050c': 'BFLB_REG_READ_ADDR_ERROR', '050d': 'BFLB_REG_READ_ERROR', '0601': 'BFLB_ECDH_PARA_ERROR', '0602': 'BFLB_ECDH_PRIVATE_KEY_ERROR', '0603': 'BFLB_ECDH_SHARED_KEY_ERROR', '0604': 'BFLB_ECDH_RANDOM_VAL_ERROR', '0605': 'BFLB_ECDH_DECRYPT_ERROR', '0606': 'BFLB_ECDH_ENCRYPT_ERROR', 'fffc': 'BFLB_PLL_ERROR', 'fffd': 'BFLB_INVASION_ERROR', 'fffe': 'BFLB_POLLING', 'ffff': 'BFLB_FAIL'} + eflash_loader_error_code = {'0000': 'BFLB SUCCESS', '0001': 'BFLB EFLASH LOADER SHAKEHAND FAIL', '0002': 'BFLB OPTION SET UNSUPPORTED', '0003': 'BFLB LOAD HELP BIN FAIL', '0004': 'BFLB RESET CPU FAIL', '0005': 'BFLB BURN RETRY FAIL', '0006': 'BFLB LOG READ FAIL', '0009': 'BFLB CONNECT OPENOCD SERVER FAIL', '000A': 'BFLB REPEAT BURN', '000B': 'BFLB CONFIG FILE NOT FOUND', '000C': 'BFLB SET CLOCK PLL FAIL', '000D': 'BFLB SET OPT FINISH FAIL', '000E': 'BFLB IMPORT PACKET FAIL', '0020': 'BFLB EFUSE READ FAIL', '0021': 'BFLB EFUSE WRITE FAIL', '0022': 'BFLB EFUSE COMPARE FAIL', '0023': 'BFLB EFUSE READ MAC ADDR FAIL', '0024': 'BFLB EFUSE WRITE MAC ADDR FAIL', '0025': 'BFLB EFUSE MAC ADDR CRC FAIL', '0030': 'BFLB READ FLASH JEDEC ID FAIL', '0031': 'BFLB READ FLASH STATUS REGISTER FAIL', '0032': 'BFLB WRITE FLASH STATUS REGISTER FAIL', '0033': 'BFLB FLASH CHIP ERASE FAIL', '0034': 'BFLB FLASH ERASE FAIL', '0035': 'BFLB FLASH READ FAIL', '0036': 'BFLB FLASH WRITE FAIL', '0037': 'BFLB FLASH WRITE CHECK FAIL', '0038': 'BFLB FLASH READ SHA FAIL', '0039': 'BFLB FLASH XIP MODE ENTER FAIL', '003A': 'BFLB FLASH XIP MODE EXIT FAIL', '003B': 'BFLB FLASH SET PARA FAIL', '003C': 'BFLB FLASH LOAD FIRMWARE BIN FAIL', '003D': 'BFLB FLASH MATCH TYPE FAIL', '003E': 'BFLB FLASH LOAD VERIFY FAIL', '003F': 'BFLB FLASH BOOT FAIL', '0040': 'BFLB FLASH CFG NOT FIT WITH BOOTHEADER', '0041': 'BFLB FLASH LOAD ROMFS FAIL', '0042': 'BFLB FLASH SWITCH BANK FAIL', '0043': 'BFLB FLASH IDENTIFY FAIL', '0044': 'BFLB FLASH CHIPERASE CONFLICT WITH SKIP MODE', '0050': 'BFLB IMG LOAD SHAKEHAND FAIL', '0051': 'BFLB IMG LOAD BOOT CHECK FAIL', '0060': 'BFLB IMG CREATE FAIL', '0061': 'BFLB IMG FILE NOT SET', '0062': 'BFLB IMG ADDR NOT SET', '0063': 'BFLB BOOTINFO ADDR NOT SET', '0064': 'BFLB AES KEY NOT SET', '0065': 'BFLB AES IV NOT SET', '0066': 'BFLB PUBLIC KEY NOT SET', '0067': 'BFLB PRIVATE KEY NOT SET', '0068': 'BFLB RAW DATA NOT NEED CREATE', '0069': 'BFLB FLASH ID NOT SUPPORT', '0070': 'BFLB FLASH ID NOT FOUND', '0071': 'BFLB ENCRYPT KEY LEN ERROR', '0072': 'BFLB AES IV LEN ERROR', '0073': 'BFLB AES IV SHOULD END WITH 00000000', '0074': 'BFLB IMG TYPE NOT FIT', '0075': 'BFLB IMG CREATE ENTER EXCEPT', '0076': 'BFLB PT NOT SET', '0077': 'BFLB AES KEY DATA OR LEN ERROR', '0078': 'BFLB AES IV DATA OR LEN ERROR', '0079': 'BFLB FACTORY IMG NOT FOUND', '007A': 'BFLB GENERATE ROMFS FAIL', '007B': 'BFLB PT PARCEL IS NULL', '007C': 'BFLB PT TABLE NOT SET', '007D': 'BFLB BOOT2 BIN NOT SET', '007E': 'BFLB FW BIN NOT SET', '007F': 'BFLB MEDIA NOT SET', '0080': 'BFLB ROMFS NOT SET', '0081': 'BFLB MFG BIN NOT SET', '0082': 'BFLB PT CHECK FAIL', '0083': 'D0 FW BIN NOT SET', '0084': 'IMTB BIN NOT SET', '0085': 'IMG LOADER BIN NOT SET', '0086': 'SBI BIN NOT SET', '0087': 'KERNEL BIN NOT SET', '0088': 'ROOTFS BIN NOT SET', '0089': 'KV BIN NOT SET', '0090': 'YOCBOOT BIN NOT SET', 'FFFF': 'BFLB BURN RETRY FAIL'} + +def convert_path(path:str) -> str: + return path.replace('\\/'.replace(os.sep, ''), os.sep) + +def printf(*args): + global local_log_data + data = '' + for arg in args: + data += str(arg) + if data: + if conf_sign: + for (key, value) in cgc.replace_name_list.items(): + data = data.replace(key, value) + now_time = datetime.datetime.now().strftime('[%H:%M:%S.%f')[:-3] + '] - ' + data = now_time + data + if local_log_en is True: + local_log_data += data + '\n' + else: + local_log_data = '' + if udp_send_log: + tid = str(threading.get_ident()) + if udp_log_local_echo: + print('[' + tid + ':]' + data.strip()) + try: + udp_socket_server.sendto((data.strip() + '\r\n').encode('utf-8'), udp_clinet_dict[tid]) + except Exception as e: + print(e) + elif qt_sign and QtCore.QThread.currentThread().objectName(): + print('[Task%s]' % str(QtCore.QThread.currentThread().objectName()) + data.strip()) + else: + print(data.strip()) + sys.stdout.flush() + +def local_log_enable(en=False): + global local_log_en + if en is True: + local_log_en = True + else: + local_log_en = False + +def local_log_save(local_path='log', key_word=''): + global local_log_data + log_dir = os.path.join(app_path, local_path) + if not os.path.exists(log_dir): + os.makedirs(log_dir) + if local_log_en is True: + try: + rq = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time())) + log_name = rq + '_' + key_word + '.log' + log_path = os.path.join(log_dir, log_name) + with codecs.open(log_path, 'w', encoding='utf-8') as fp: + fp.write(local_log_data) + except Exception as e: + printf(e) + traceback.print_exc(limit=5, file=sys.stdout) + local_log_data = '' + +def get_bflb_error_code(code): + return bflb_error_code[code] + +def clear_global(): + global error_code_num, error_code_num_task + error_code_num = 'FFFF' + error_code_num_task = [] + +def set_error_code(num_str, task=None): + global error_code_num + if task != None: + if len(error_code_num_task) == 0: + for i in range(66): + error_code_num_task.append('FFFF') + if error_code_num_task[task] == 'FFFF': + error_code_num_task[task] = num_str + if num_str == 'FFFF': + error_code_num_task[task] = num_str + else: + if error_code_num == 'FFFF': + error_code_num = num_str + if num_str == 'FFFF': + error_code_num = num_str + +def get_error_code(task=None): + if task != None: + return error_code_num_task[task] + return error_code_num + +def errorcode_msg(task=None): + if task != None: + return '{"ErrorCode": "' + error_code_num_task[task] + '", "ErrorMsg":"' + eflash_loader_error_code[error_code_num_task[task]] + '"}' + return '{"ErrorCode": "' + error_code_num + '", "ErrorMsg":"' + eflash_loader_error_code[error_code_num] + '"}' + +def get_security_key(): + return (hexstr_to_bytearray('424F554646414C4F4C41424B45594956'), hexstr_to_bytearray('424F554646414C4F4C41424B00000000')) + +def hexstr_to_bytearray_b(hexstring): + return bytearray.fromhex(hexstring) + +def hexstr_to_bytearray(hexstring): + return bytearray.fromhex(hexstring) + +def hexstr_to_bytearray_l(hexstring): + b = bytearray.fromhex(hexstring) + b.reverse() + return b + +def int_to_2bytearray_l(intvalue): + return struct.pack('H', intvalue) + +def int_to_4bytearray_l(intvalue): + src = bytearray(4) + src[3] = intvalue >> 24 & 255 + src[2] = intvalue >> 16 & 255 + src[1] = intvalue >> 8 & 255 + src[0] = intvalue >> 0 & 255 + return src + +def int_to_4bytearray_b(intvalue): + val = int_to_4bytearray_l(intvalue) + val.reverse() + return val + +def bytearray_reverse(a): + length = len(a) + b = bytearray(length) + i = 0 + while i < length: + b[i] = a[length - i - 1] + i = i + 1 + return b + +def bytearray_to_int(b): + return int(binascii.hexlify(b), 16) + +def string_to_bytearray(string): + return bytes(string, encoding='utf8') + +def bytearray_to_str(bytesarray): + return str(bytesarray) + +def get_random_hexstr(n_bytes): + hextring = '' + i = 0 + while i < n_bytes: + hextring = hextring + str(binascii.hexlify(random.randint(0, 255))) + i = i + 1 + return hextring + +def get_crc32_bytearray(data): + crc = binascii.crc32(data) + return int_to_4bytearray_l(crc) + +def add_to_16(par): + while len(par) % 16 != 0: + par += b'\x00' + return par + +def enable_udp_send_log(local_echo): + global udp_send_log, udp_log_local_echo, udp_socket_server + udp_send_log = True + udp_log_local_echo = local_echo + udp_socket_server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +def add_udp_client(tid, upd_client): + udp_clinet_dict[tid] = upd_client + +def remove_udp_client(tid): + del udp_clinet_dict[tid] + +def update_cfg(cfg, section, key, value): + if cfg.has_option(section, key): + cfg.set(section, key, str(value)) + +def get_byte_array(string): + return string.encode('utf-8') + +def verify_hex_num(string): + length = len(string) + i = 0 + while True: + if re.match('\\A[0-9a-fA-F]+\\Z', string[i:i + 1]) is None: + return False + i += 1 + if i >= length: + break + return True + +def get_eflash_loader(xtal): + xtal_suffix = str(xtal).lower().replace('.', 'p').replace('M', 'm').replace('RC', 'rc') + return 'eflash_loader_' + xtal_suffix + '.bin' + +def str_endian_switch(string): + s = string[6:8] + string[4:6] + string[2:4] + string[0:2] + return s + +def img_create_sha256_data(data_bytearray): + hashfun = hashlib.sha256() + hashfun.update(data_bytearray) + return hexstr_to_bytearray(hashfun.hexdigest()) + +def img_create_encrypt_data(data_bytearray, key_bytearray, iv_bytearray, flash_img): + if flash_img == 0: + cryptor = AES.new(key_bytearray, AES.MODE_CBC, iv_bytearray) + ciphertext = cryptor.encrypt(data_bytearray) + else: + iv = Counter.new(128, initial_value=int(binascii.hexlify(iv_bytearray), 16)) + cryptor = AES.new(key_bytearray, AES.MODE_CTR, counter=iv) + ciphertext = cryptor.encrypt(data_bytearray) + return ciphertext + +def aes_decrypt_data(data, key_bytearray, iv_bytearray, flash_img): + if flash_img == 0: + cryptor = AES.new(key_bytearray, AES.MODE_CBC, iv_bytearray) + plaintext = cryptor.decrypt(data) + else: + iv = Counter.new(128, initial_value=int(binascii.hexlify(iv_bytearray), 16)) + cryptor = AES.new(key_bytearray, AES.MODE_CTR, counter=iv) + plaintext = cryptor.decrypt(data) + return plaintext + +def open_file(file, mode='rb'): + fp = open(os.path.join(app_path, file), mode) + return fp + +def copyfile(srcfile, dstfile): + if os.path.isfile(srcfile): + (fpath, fname) = os.path.split(dstfile) + if not os.path.exists(fpath): + os.makedirs(fpath) + shutil.copyfile(srcfile, dstfile) + else: + printf('Src file not exists') + +def get_systype(): + type_ = platform.system().lower() + arch = platform.machine().lower() + if type_ == 'windows': + arch = 'amd64' if platform.architecture()[0] == '64bit' else 'x86' + if arch: + return '%s_%s' % (type_, arch) + return type_ + +def get_serial_ports(): + try: + from serial.tools.list_ports import comports + except ImportError: + return + WINDOWS = sys.platform.startswith('win') + result = [] + for (p, d, h) in comports(): + if not p: + continue + if WINDOWS and PY2: + try: + d = unicode(d, errors='ignore') + except TypeError: + pass + if 'VID:PID' in h: + result.append({'port': p, 'description': d, 'hwid': h}) + if not result: + if 'darwin' in get_systype(): + for p in glob('/dev/tty.*'): + result.append({'port': p, 'description': 'n/a', 'hwid': 'n/a'}) + return result + +def serial_enumerate(): + prog_ports = [] + sdio_ports = [] + sdio_file_ser_dict = {} + uart_ports = [] + file_dict = {} + ports = [] + if sys.platform.startswith('win'): + for (p, d, h) in comports(): + if not ('Virtual' in d or not p) and 'STM' not in d: + continue + if 'PID=1D6B' in h.upper(): + ser_value = h.split(' ')[2][4:] + if ser_value not in sdio_file_ser_dict: + sdio_file_ser_dict[ser_value] = p + elif 'LOCATION' in h.upper(): + file_dict[sdio_file_ser_dict[ser_value]] = p + sdio_ports.append(sdio_file_ser_dict[ser_value] + ' (SDIO)') + else: + file_dict[p] = sdio_file_ser_dict[ser_value] + sdio_ports.append(p + ' (SDIO)') + if 'FACTORYAIOT_PROG' in h.upper() or 'PID=42BF:B210' in h.upper(): + prog_ports.append(p + ' (PROG)') + else: + uart_ports.append(p) + elif 'FACTORYAIOT_PROG' in h.upper() or 'PID=42BF:B210' in h.upper(): + prog_ports.append(p + ' (PROG)') + else: + uart_ports.append(p) + try: + uart_ports = sorted(uart_ports, key=lambda x: int(re.match('COM(\\d+)', x).group(1))) + except Exception: + uart_ports = sorted(uart_ports) + ports = sorted(prog_ports) + sorted(sdio_ports) + uart_ports + elif sys.platform.startswith('linux'): + for (p, d, h) in comports(): + if not p: + continue + if 'PID=1D6B' in h.upper(): + ser_value = h.split(' ')[2][4:] + if ser_value not in sdio_file_ser_dict: + sdio_file_ser_dict[ser_value] = p + elif sdio_file_ser_dict[ser_value] > p: + file_dict[p] = sdio_file_ser_dict[ser_value] + sdio_ports.append(p + ' (SDIO)') + else: + file_dict[sdio_file_ser_dict[ser_value]] = p + sdio_ports.append(sdio_file_ser_dict[ser_value] + ' (SDIO)') + elif 'FACTORYAIOT PROG' in h.upper(): + prog_ports.append(p + ' (PROG)') + else: + uart_ports.append(p) + ports = sorted(prog_ports) + sorted(sdio_ports) + sorted(uart_ports) + elif sys.platform.startswith('darwin'): + for dev in glob('/dev/tty.usb*'): + ports.append(dev) + return ports + +def pylink_enumerate(): + try: + if sys.platform == 'win32': + obj_dll = pylink.Library(dllpath=path_dll) + obj = pylink.JLink(lib=obj_dll) + else: + obj = pylink.JLink() + except Exception: + return [] + else: + return obj.connected_emulators() + +def cklink_openocd_enumerate(): + ports_cklink = [] + ports_openocd = [] + if sys.platform.startswith('win'): + for (p, d, h) in comports(): + if not p: + continue + if 'FACTORYAIOT_PROG' in h.upper(): + match1 = re.search('FACTORYAIOT_PROG_([a-zA-Z0-9]{6}) LOCATION', h.upper(), re.I) + match2 = re.search('FACTORYAIOT_PROG_([a-zA-Z0-9]{6})$', h.upper(), re.I) + if match1 is not None: + ports_cklink.append(match1.group(1)) + if match2 is not None: + ports_openocd.append(match2.group(1)) + elif sys.platform.startswith('linux'): + for (p, d, h) in comports(): + if not p: + continue + if 'FactoryAIOT Prog' in h: + match1 = re.search('FactoryAIOT Prog ([a-zA-Z0-9]{6}) LOCATION', h, re.I) + if match1 is not None: + ports_cklink.append(match1.group(1)) + return (ports_cklink, ports_openocd) + +def image_create_parser_init(): + parser = argparse.ArgumentParser(description='bouffalolab image create command') + parser.add_argument('--chipname', dest='chipname', help='chip name') + parser.add_argument('--imgfile', dest='imgfile', help='image file') + parser.add_argument('--security', dest='security', help='security save efusedata') + parser.add_argument('-i', '--image', dest='image', help='image type: media or if') + parser.add_argument('-c', '--cpu', dest='cpu', help='cpu type: cpu0 cpu1 or all') + parser.add_argument('-g', '--group', dest='group', help='group type') + parser.add_argument('-s', '--signer', dest='signer', help='signer') + return parser + +def eflash_loader_parser_init(): + parser = argparse.ArgumentParser(description='bouffalolab eflash loader command') + parser.add_argument('--chipname', dest='chipname', help='chip name') + parser.add_argument('--chipid', dest='chipid', action='store_true', help='chip id') + parser.add_argument('--usage', dest='usage', action='store_true', help='display usage') + parser.add_argument('--flash', dest='flash', action='store_true', help='target is flash') + parser.add_argument('--efuse', dest='efuse', action='store_true', help='target is efuse') + parser.add_argument('--ram', dest='ram', action='store_true', help='target is ram') + parser.add_argument('-w', '--write', dest='write', action='store_true', help='write to flash/efuse') + parser.add_argument('-e', '--erase', dest='erase', action='store_true', help='erase flash') + parser.add_argument('-r', '--read', dest='read', action='store_true', help='read from flash/efuse') + parser.add_argument('-n', '--none', dest='none', action='store_true', help='eflash loader environment init') + parser.add_argument('-p', '--port', dest='port', help='serial port to use') + parser.add_argument('-b', '--baudrate', dest='baudrate', type=int, help='the speed at which to communicate') + parser.add_argument('-c', '--config', dest='config', help='eflash loader config file') + parser.add_argument('-i', '--interface', dest='interface', help='interface type: uart/jlink/openocd') + parser.add_argument('--xtal', dest='xtal', help='xtal type') + parser.add_argument('--start', dest='start', help='start address') + parser.add_argument('--end', dest='end', help='end address') + parser.add_argument('--addr', dest='addr', help='address to write') + parser.add_argument('--mac', dest='mac', help='mac address to write') + parser.add_argument('--file', dest='file', help='file to store read data or file to write') + parser.add_argument('--skip', dest='skip', help='skip write file to flash') + parser.add_argument('--packet', dest='packet', help=' import packet to replace burn file') + parser.add_argument('--efusefile', dest='efusefile', help='efuse file to write efuse') + parser.add_argument('--data', dest='data', help='data to write') + parser.add_argument('--mass', dest='mass', help='load mass bin') + parser.add_argument('--loadstr', dest='loadstr', help='') + parser.add_argument('--loadfile', dest='loadfile', help='') + parser.add_argument('--userarea', dest='userarea', help='user area') + parser.add_argument('--romfs', dest='romfs', help='romfs data to write') + parser.add_argument('--csvfile', dest='csvfile', help='csv file contains 3/5 tuples') + parser.add_argument('--csvaddr', dest='csvaddr', help='address to write for csv file') + parser.add_argument('--para', dest='para', help='efuse para') + parser.add_argument('--isp', dest='isp', action='store_true', help='isp config') + parser.add_argument('--createcfg', dest='createcfg', help='img create cfg file') + parser.add_argument('--key', dest='key', help='aes key for socket') + parser.add_argument('--ecdh', dest='ecdh', action='store_true', help='open ecdh function') + parser.add_argument('--echo', dest='echo', action='store_true', help='open local log echo') + parser.add_argument('-a', '--auto', dest='auto', action='store_true', help='auto flash') + parser.add_argument('-v', '--version', dest='version', action='store_true', help='display version') + return parser + diff --git a/pylink/__init__.py b/pylink/__init__.py new file mode 100644 index 0000000..d3bd4d5 --- /dev/null +++ b/pylink/__init__.py @@ -0,0 +1,21 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/__init__.py +__version__ = '0.5.0' +__title__ = 'pylink' +__author__ = 'Square Embedded Software Team' +__author_email__ = 'esw-team@squareup.com' +__copyright__ = 'Copyright 2017 Square, Inc.' +__license__ = 'Apache 2.0' +__url__ = 'http://www.github.com/Square/pylink' +__description__ = 'Python interface for SEGGER J-Link.' +__long_description__ = "This module provides a Python implementation of the\nJ-Link SDK by leveraging the SDK's DLL.\n" +from .enums import * +from .errors import * +from .jlink import * +from .library import * +from .structs import * +from .unlockers import * +# okay decompiling ./pylink/__init__.pyc diff --git a/pylink/binpacker.py b/pylink/binpacker.py new file mode 100644 index 0000000..1635e3b --- /dev/null +++ b/pylink/binpacker.py @@ -0,0 +1,57 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/binpacker.py +import ctypes, math +BITS_PER_BYTE = 8 + +def pack_size(value): + """Returns the number of bytes required to represent a given value. + + Args: + value (int): the natural number whose size to get + + Returns: + The minimal number of bytes required to represent the given integer. + + Raises: + ValueError: if ``value < 0``. + TypeError: if ``value`` is not a number. + """ + if value == 0: + return 1 + if value < 0: + raise ValueError('Expected non-negative integer.') + return int(math.log(value, 256)) + 1 + + +def pack(value, nbits=None): + """Packs a given value into an array of 8-bit unsigned integers. + + If ``nbits`` is not present, calculates the minimal number of bits required + to represent the given ``value``. The result is little endian. + + Args: + value (int): the integer value to pack + nbits (int): optional number of bits to use to represent the value + + Returns: + An array of ``ctypes.c_uint8`` representing the packed ``value``. + + Raises: + ValueError: if ``value < 0`` and ``nbits`` is ``None`` or ``nbits <= 0``. + TypeError: if ``nbits`` or ``value`` are not numbers. + """ + if nbits is None: + nbits = pack_size(value) * BITS_PER_BYTE + else: + if nbits <= 0: + raise ValueError('Given number of bits must be greater than 0.') + buf_size = int(math.ceil(nbits / float(BITS_PER_BYTE))) + buf = (ctypes.c_uint8 * buf_size)() + for idx, _ in enumerate(buf): + buf[idx] = value >> idx * BITS_PER_BYTE & 255 + + return buf +# okay decompiling ./pylink/binpacker.pyc diff --git a/pylink/decorators.py b/pylink/decorators.py new file mode 100644 index 0000000..c1b730f --- /dev/null +++ b/pylink/decorators.py @@ -0,0 +1,52 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/decorators.py +from . import threads +import functools + +def async_decorator(func): + """Asynchronous function decorator. Interprets the function as being + asynchronous, so returns a function that will handle calling the + Function asynchronously. + + Args: + func (function): function to be called asynchronously + + Returns: + The wrapped function. + + Raises: + AttributeError: if ``func`` is not callable + """ + + @functools.wraps(func) + def async_wrapper(*args, **kwargs): + if not ('callback' not in kwargs or kwargs['callback']): + return func(*args, **kwargs) + callback = kwargs.pop('callback') + if not callable(callback): + raise TypeError("Expected 'callback' is not callable.") + + def thread_func(*args, **kwargs): + exception, res = (None, None) + try: + res = func(*args, **kwargs) + except Exception as e: + try: + exception = e + finally: + e = None + del e + + return callback(exception, res) + + thread = threads.ThreadReturn(target=thread_func, args=args, + kwargs=kwargs) + thread.daemon = True + thread.start() + return thread + + return async_wrapper +# okay decompiling ./pylink/decorators.pyc diff --git a/pylink/enums.py b/pylink/enums.py new file mode 100644 index 0000000..acd6eab --- /dev/null +++ b/pylink/enums.py @@ -0,0 +1,528 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/enums.py +import ctypes + +class JLinkGlobalErrors(object): + __doc__ = 'Enumeration for the error codes which any J-Link SDK DLL API-function\n can have as a return value.' + UNSPECIFIED_ERROR = -1 + EMU_NO_CONNECTION = -256 + EMU_COMM_ERROR = -257 + DLL_NOT_OPEN = -258 + VCC_FAILURE = -259 + INVALID_HANDLE = -260 + NO_CPU_FOUND = -261 + EMU_FEATURE_UNSUPPORTED = -262 + EMU_NO_MEMORY = -263 + TIF_STATUS_ERROR = -264 + FLASH_PROG_COMPARE_FAILED = -265 + FLASH_PROG_PROGRAM_FAILED = -266 + FLASH_PROG_VERIFY_FAILED = -267 + OPEN_FILE_FAILED = -268 + UNKNOWN_FILE_FORMAT = -269 + WRITE_TARGET_MEMORY_FAILED = -270 + DEVICE_FEATURE_NOT_SUPPORTED = -271 + WRONG_USER_CONFIG = -272 + NO_TARGET_DEVICE_SELECTED = -273 + CPU_IN_LOW_POWER_MODE = -274 + + @classmethod + def to_string(cls, error_code): + """Returns the string message for the given ``error_code``. + + Args: + cls (JlinkGlobalErrors): the ``JLinkGlobalErrors`` class + error_code (int): error code to convert + + Returns: + An error string corresponding to the error code. + + Raises: + ValueError: if the error code is invalid. + """ + if error_code == cls.EMU_NO_CONNECTION: + return 'No connection to emulator.' + if error_code == cls.EMU_COMM_ERROR: + return 'Emulator connection error.' + if error_code == cls.DLL_NOT_OPEN: + return "DLL has not been opened. Did you call '.connect()'?" + if error_code == cls.VCC_FAILURE: + return 'Target system has no power.' + if error_code == cls.INVALID_HANDLE: + return 'Given file / memory handle is invalid.' + if error_code == cls.NO_CPU_FOUND: + return 'Could not find supported CPU.' + if error_code == cls.EMU_FEATURE_UNSUPPORTED: + return 'Emulator does not support the selected feature.' + if error_code == cls.EMU_NO_MEMORY: + return 'Emulator out of memory.' + if error_code == cls.TIF_STATUS_ERROR: + return 'Target interface error.' + if error_code == cls.FLASH_PROG_COMPARE_FAILED: + return 'Programmed data differs from source data.' + if error_code == cls.FLASH_PROG_PROGRAM_FAILED: + return 'Programming error occured.' + if error_code == cls.FLASH_PROG_VERIFY_FAILED: + return 'Error while verifying programmed data.' + if error_code == cls.OPEN_FILE_FAILED: + return 'Specified file could not be opened.' + if error_code == cls.UNKNOWN_FILE_FORMAT: + return 'File format of selected file is not supported.' + if error_code == cls.WRITE_TARGET_MEMORY_FAILED: + return 'Could not write target memory.' + if error_code == cls.DEVICE_FEATURE_NOT_SUPPORTED: + return 'Feature not supported by connected device.' + if error_code == cls.WRONG_USER_CONFIG: + return 'User configured DLL parameters incorrectly.' + if error_code == cls.NO_TARGET_DEVICE_SELECTED: + return 'User did not specify core to connect to.' + if error_code == cls.CPU_IN_LOW_POWER_MODE: + return 'Target CPU is in low power mode.' + if error_code == cls.UNSPECIFIED_ERROR: + return 'Unspecified error.' + raise ValueError('Invalid error code: %d' % error_code) + + +class JLinkEraseErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for the error codes generated during an erase operation.' + ILLEGAL_COMMAND = -5 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.ILLEGAL_COMMAND: + return 'Failed to erase sector.' + return super(JLinkEraseErrors, cls).to_string(error_code) + + +class JLinkFlashErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for the error codes generated during a flash operation.' + COMPARE_ERROR = -2 + PROGRAM_ERASE_ERROR = -3 + VERIFICATION_ERROR = -4 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.COMPARE_ERROR: + return 'Error comparing flash content to programming data.' + if error_code == cls.PROGRAM_ERASE_ERROR: + return 'Error during program/erase phase.' + if error_code == cls.VERIFICATION_ERROR: + return 'Error verifying programmed data.' + return super(JLinkFlashErrors, cls).to_string(error_code) + + +class JLinkWriteErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for the error codes generated during a write.' + ZONE_NOT_FOUND_ERROR = -5 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.ZONE_NOT_FOUND_ERROR: + return 'Zone not found' + return super(JLinkWriteErrors, cls).to_string(error_code) + + +class JLinkReadErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for the error codes generated during a read.' + ZONE_NOT_FOUND_ERROR = -5 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.ZONE_NOT_FOUND_ERROR: + return 'Zone not found' + return super(JLinkReadErrors, cls).to_string(error_code) + + +class JLinkDataErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for the error codes generated when setting a data event.' + ERROR_UNKNOWN = 2147483648 + ERROR_NO_MORE_EVENTS = 2147483649 + ERROR_NO_MORE_ADDR_COMP = 2147483650 + ERROR_NO_MORE_DATA_COMP = 2147483652 + ERROR_INVALID_ADDR_MASK = 2147483680 + ERROR_INVALID_DATA_MASK = 2147483712 + ERROR_INVALID_ACCESS_MASK = 2147483776 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.ERROR_UNKNOWN: + return 'Unknown error.' + if error_code == cls.ERROR_NO_MORE_EVENTS: + return 'There are no more available watchpoint units.' + if error_code == cls.ERROR_NO_MORE_ADDR_COMP: + return 'No more address comparisons can be set.' + if error_code == cls.ERROR_NO_MORE_DATA_COMP: + return 'No more data comparisons can be set.' + if error_code == cls.ERROR_INVALID_ADDR_MASK: + return 'Invalid flags passed for the address mask.' + if error_code == cls.ERROR_INVALID_DATA_MASK: + return 'Invalid flags passed for the data mask.' + if error_code == cls.ERROR_INVALID_ACCESS_MASK: + return 'Invalid flags passed for the access mask.' + return super(JLinkDataErrors, cls).to_string(error_code) + + +class JLinkRTTErrors(JLinkGlobalErrors): + __doc__ = 'Enumeration for error codes from RTT.' + RTT_ERROR_CONTROL_BLOCK_NOT_FOUND = -2 + + @classmethod + def to_string(cls, error_code): + if error_code == cls.RTT_ERROR_CONTROL_BLOCK_NOT_FOUND: + return 'The RTT Control Block has not yet been found (wait?)' + return super(JLinkRTTErrors, cls).to_string(error_code) + + +class JLinkHost(object): + __doc__ = 'Enumeration for the different JLink hosts: currently only IP and USB.' + USB = 1 + IP = 2 + USB_OR_IP = USB | IP + + +class JLinkInterfaces(object): + __doc__ = 'Target interfaces for the J-Link.' + JTAG = 0 + SWD = 1 + FINE = 3 + ICSP = 4 + SPI = 5 + C2 = 6 + + +class JLinkResetStrategyCortexM3(object): + __doc__ = 'Target reset strategies for the J-Link.\n\n Attributes:\n NORMAL: default reset strategy, does whatever is best to reset.\n CORE: only the core is reset via the ``VECTRESET`` bit.\n RESETPIN: pulls the reset pin low to reset the core and peripherals.\n CONNECT_UNDER_RESET: J-Link connects to target while keeping reset\n active. This is recommented for STM32 devices.\n HALT_AFTER_BTL: halt the core after the bootloader is executed.\n HALT_BEFORE_BTL: halt the core before the bootloader is executed.\n KINETIS: performs a normal reset, but also disables the watchdog.\n ADI_HALT_AFTER_KERNEL: sets the ``SYSRESETREQ`` bit in the ``AIRCR`` in\n order to reset the device.\n CORE_AND_PERIPHERALS: sets the ``SYSRESETREQ`` bit in the ``AIRCR``, and\n the ``VC_CORERESET`` bit in the ``DEMCR`` to make sure that the CPU is\n halted immediately after reset.\n LPC1200: reset for LPC1200 devices.\n S3FN60D: reset for Samsung S3FN60D devices.\n\n Note:\n Please see the J-Link SEGGER Documentation, UM8001, for full information\n about the different reset strategies.\n ' + NORMAL = 0 + CORE = 1 + RESETPIN = 2 + CONNECT_UNDER_RESET = 3 + HALT_AFTER_BTL = 4 + HALT_BEFORE_BTL = 5 + KINETIS = 6 + ADI_HALT_AFTER_KERNEL = 7 + CORE_AND_PERIPHERALS = 8 + LPC1200 = 9 + S3FN60D = 10 + + +class JLinkFunctions(object): + __doc__ = 'Collection of function prototype and type builders for the J-Link SDK\n API calls.' + LOG_PROTOTYPE = ctypes.CFUNCTYPE(None, ctypes.c_char_p) + UNSECURE_HOOK_PROTOTYPE = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_uint32) + FLASH_PROGRESS_PROTOTYPE = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_char_p, ctypes.c_int) + + +class JLinkCore(object): + __doc__ = 'Enumeration for the different CPU core identifiers.\n\n These are the possible cores for targets the J-Link is connected to.\n Note that these are bitfields.' + NONE = 0 + ANY = 4294967295 + CORTEX_M1 = 16777471 + COLDFIRE = 50331647 + CORTEX_M3 = 50331903 + CORTEX_M3_R1P0 = 50331664 + CORTEX_M3_R1P1 = 50331665 + CORTEX_M3_R2P0 = 50331680 + SIM = 83886079 + XSCALE = 100663295 + CORTEX_M0 = 100663551 + CORTEX_M_V8BASEL = 100729087 + ARM7 = 134217727 + ARM7TDMI = 117440767 + ARM7TDMI_R3 = 117440575 + ARM7TDMI_R4 = 117440591 + ARM7TDMI_S = 117441023 + ARM7TDMI_S_R3 = 117440831 + ARM7TDMI_S_R4 = 117440847 + CORTEX_A8 = 134217983 + CORTEX_A7 = 134742271 + CORTEX_A9 = 134807807 + CORTEX_A12 = 134873343 + CORTEX_A15 = 134938879 + CORTEX_A17 = 135004415 + ARM9 = 167772159 + ARM9TDMI_S = 150995455 + ARM920T = 153092351 + ARM922T = 153223423 + ARM926EJ_S = 153485823 + ARM946E_S = 155582975 + ARM966E_S = 157680127 + ARM968E_S = 157811199 + ARM11 = 201326591 + ARM1136 = 188153855 + ARM1136J = 188089087 + ARM1136J_S = 188089343 + ARM1136JF = 188090111 + ARM1136JF_S = 188090367 + ARM1156 = 190251007 + ARM1176 = 192348159 + ARM1176J = 192283391 + ARM1176J_S = 192283647 + ARM1176JF = 192284415 + ARM1176JF_S = 192284671 + CORTEX_R4 = 201326847 + CORTEX_R5 = 201392383 + RX = 234881023 + RX610 = 218169343 + RX62N = 218234879 + RX62T = 218300415 + RX63N = 218365951 + RX630 = 218431487 + RX63T = 218497023 + RX621 = 218562559 + RX62G = 218628095 + RX631 = 218693631 + RX210 = 219217919 + RX21A = 219283455 + RX220 = 219348991 + RX230 = 219414527 + RX231 = 219480063 + RX23T = 219545599 + RX111 = 220266495 + RX110 = 220332031 + RX113 = 220397567 + RX64M = 221315071 + RX71M = 221380607 + CORTEX_M4 = 234881279 + CORTEX_M7 = 234946815 + CORTEX_M_V8MAINL = 235012351 + CORTEX_A5 = 251658495 + POWER_PC = 285212671 + POWER_PC_N1 = 285147391 + POWER_PC_N2 = 285147647 + MIPS = 301989887 + MIPS_M4K = 285278207 + MIPS_MICROAPTIV = 285343743 + EFM8_UNSPEC = 318767103 + CIP51 = 302055423 + + +class JLinkDeviceFamily(object): + __doc__ = 'Enumeration for the difference device families.\n\n These are the possible device families for targets that the J-Link is\n connected to.' + AUTO = 0 + CORTEX_M1 = 1 + COLDFIRE = 2 + CORTEX_M3 = 3 + SIMULATOR = 4 + XSCALE = 5 + CORTEX_M0 = 6 + ARM7 = 7 + CORTEX_A8 = 8 + CORTEX_A9 = 8 + ARM9 = 9 + ARM10 = 10 + ARM11 = 11 + CORTEX_R4 = 12 + RX = 13 + CORTEX_M4 = 14 + CORTEX_A5 = 15 + POWERPC = 16 + MIPS = 17 + EFM8 = 18 + ANY = 255 + + +class JLinkFlags(object): + __doc__ = 'Enumeration for the different flags that are passed to the J-Link C SDK\n API methods.' + GO_OVERSTEP_BP = 1 + DLG_BUTTON_YES = 1 + DLG_BUTTON_NO = 2 + DLG_BUTTON_OK = 4 + DLG_BUTTON_CANCEL = 8 + HW_PIN_STATUS_LOW = 0 + HW_PIN_STATUS_HIGH = 1 + HW_PIN_STATUS_UNKNOWN = 255 + + +class JLinkSWOInterfaces(object): + __doc__ = 'Serial Wire Output (SWO) interfaces.' + UART = 0 + MANCHESTER = 1 + + +class JLinkSWOCommands(object): + __doc__ = 'Serial Wire Output (SWO) commands.' + START = 0 + STOP = 1 + FLUSH = 2 + GET_SPEED_INFO = 3 + GET_NUM_BYTES = 10 + SET_BUFFERSIZE_HOST = 20 + SET_BUFFERSIZE_EMU = 21 + + +class JLinkCPUCapabilities(object): + __doc__ = 'Target CPU Cabilities.' + READ_MEMORY = 2 + WRITE_MEMORY = 4 + READ_REGISTERS = 8 + WRITE_REGISTERS = 16 + GO = 32 + STEP = 64 + HALT = 128 + IS_HALTED = 256 + RESET = 512 + RUN_STOP = 1024 + TERMINAL = 2048 + DCC = 16384 + HSS = 32768 + + +class JLinkHaltReasons(object): + __doc__ = 'Halt reasons for the CPU.\n\n Attributes:\n DBGRQ: CPU has been halted because DBGRQ signal asserted.\n CODE_BREAKPOINT: CPU has been halted because of code breakpoint match.\n DATA_BREAKPOINT: CPU has been halted because of data breakpoint match.\n VECTOR_CATCH: CPU has been halted because of vector catch.\n ' + DBGRQ = 0 + CODE_BREAKPOINT = 1 + DATA_BREAKPOINT = 2 + VECTOR_CATCH = 3 + + +class JLinkVectorCatchCortexM3(object): + __doc__ = 'Vector catch types for the ARM Cortex M3.\n\n Attributes:\n CORE_RESET: The CPU core reset.\n MEM_ERROR: A memory management error occurred.\n COPROCESSOR_ERROR: Usage fault error accessing the Coprocessor.\n CHECK_ERROR: Usage fault error on enabled check.\n STATE_ERROR: Usage fault state error.\n BUS_ERROR: Normal bus error.\n INT_ERROR: Interrupt or exception service error.\n HARD_ERROR: Hard fault error.\n ' + CORE_RESET = 1 + MEM_ERROR = 16 + COPROCESSOR_ERROR = 32 + CHECK_ERROR = 64 + STATE_ERROR = 128 + BUS_ERROR = 256 + INT_ERROR = 512 + HARD_ERROR = 1024 + + +class JLinkBreakpoint(object): + __doc__ = 'J-Link breakpoint types.\n\n Attributes:\n SW_RAM: Software breakpont located in RAM.\n SW_FLASH: Software breakpoint located in flash.\n SW: Software breakpoint located in RAM or flash.\n HW: Hardware breakpoint.\n ANY: Allows specifying any time of breakpoint.\n ARM: Breakpoint in ARM mode (only available on ARM 7/9 cores).\n THUMB: Breakpoint in THUMB mode (only available on ARM 7/9 cores).\n ' + SW_RAM = 16 + SW_FLASH = 32 + SW = 240 + HW = 4294967040 + ANY = 4294967280 + ARM = 1 + THUMB = 2 + + +class JLinkBreakpointImplementation(object): + __doc__ = 'J-Link breakpoint implementation types.\n\n Attributes:\n HARD: Hardware breakpoint using a breakpoint unit.\n SOFT: Software breakpoint using a breakpoint instruction.\n PENDING: Breakpoint has not been set yet.\n FLASH: Breakpoint set in flash.\n ' + HARD = 1 + SOFT = 2 + PENDING = 4 + FLASH = 16 + + +class JLinkEventTypes(object): + __doc__ = 'J-Link data event types.\n\n Attributes:\n BREAKPOINT: breakpoint data event.\n ' + BREAKPOINT = 1 + + +class JLinkAccessFlags(object): + __doc__ = 'J-Link access types for data events.\n\n These access types allow specifying the different types of access events\n that should be monitored.\n\n Attributes:\n READ: specifies to monitor read accesses.\n WRITE: specifies to monitor write accesses.\n PRIVILEGED: specifies to monitor privileged accesses.\n SIZE_8BIT: specifies to monitor an 8-bit access width.\n SIZE_16BIT: specifies to monitor an 16-bit access width.\n SIZE_32BIT: specifies to monitor an 32-bit access width.\n ' + READ = 0 + WRITE = 1 + PRIV = 16 + SIZE_8BIT = 0 + SIZE_16BIT = 2 + SIZE_32BIT = 4 + + +class JLinkAccessMaskFlags(object): + __doc__ = 'J-Link access mask flags.\n\n Attributes:\n SIZE: specifies to not care about the access size of the event.\n DIR: specifies to not care about the access direction of the event.\n PRIV: specifies to not care about the access privilege of the event.\n ' + SIZE = 6 + DIR = 1 + PRIV = 16 + + +class JLinkStraceCommand(object): + __doc__ = 'STRACE commmands.' + TRACE_EVENT_SET = 0 + TRACE_EVENT_CLR = 1 + TRACE_EVENT_CLR_ALL = 2 + SET_BUFFER_SIZE = 3 + + +class JLinkStraceEvent(object): + __doc__ = 'STRACE events.' + CODE_FETCH = 0 + DATA_ACCESS = 1 + DATA_LOAD = 2 + DATA_STORE = 3 + + +class JLinkStraceOperation(object): + __doc__ = 'STRACE operation specifiers.' + TRACE_START = 0 + TRACE_STOP = 1 + TRACE_INCLUDE_RANGE = 2 + TRACE_EXCLUDE_RANGE = 3 + + +class JLinkTraceSource(object): + __doc__ = 'Sources for tracing.' + ETB = 0 + ETM = 1 + MTB = 2 + + +class JLinkTraceCommand(object): + __doc__ = 'J-Link trace commands.' + START = 0 + STOP = 1 + FLUSH = 2 + GET_NUM_SAMPLES = 16 + GET_CONF_CAPACITY = 17 + SET_CAPACITY = 18 + GET_MIN_CAPACITY = 19 + GET_MAX_CAPACITY = 20 + SET_FORMAT = 32 + GET_FORMAT = 33 + GET_NUM_REGIONS = 48 + GET_REGION_PROPS = 49 + GET_REGION_PROPS_EX = 50 + + +class JLinkTraceFormat(object): + __doc__ = 'J-Link trace formats.\n\n Attributes:\n FORMAT_4BIT: 4-bit data.\n FORMAT_8BIT: 8-bit data.\n FORMAT_16BIT: 16-bit data.\n FORMAT_MULTIPLEXED: multiplexing on ETM / buffer link.\n FORMAT_DEMULTIPLEXED: de-multiplexing on ETM / buffer link.\n FORMAT_DOUBLE_EDGE: clock data on both ETM / buffer link edges.\n FORMAT_ETM7_9: ETM7/ETM9 protocol.\n FORMAT_ETM10: ETM10 protocol.\n FORMAT_1BIT: 1-bit data.\n FORMAT_2BIT: 2-bit data.\n ' + FORMAT_4BIT = 1 + FORMAT_8BIT = 2 + FORMAT_16BIT = 4 + FORMAT_MULTIPLEXED = 8 + FORMAT_DEMULTIPLEXED = 16 + FORMAT_DOUBLE_EDGE = 32 + FORMAT_ETM7_9 = 64 + FORMAT_ETM10 = 128 + FORMAT_1BIT = 256 + FORMAT_2BIT = 512 + + +class JLinkROMTable(object): + __doc__ = 'The J-Link ROM tables.' + NONE = 256 + ETM = 257 + MTB = 258 + TPIU = 259 + ITM = 260 + DWT = 261 + FPB = 262 + NVIC = 263 + TMC = 264 + TF = 265 + PTM = 266 + ETB = 267 + DBG = 268 + APBAP = 269 + AHBAP = 270 + SECURE = 271 + + +class JLinkRTTCommand(object): + __doc__ = 'RTT commands.' + START = 0 + STOP = 1 + GETDESC = 2 + GETNUMBUF = 3 + GETSTAT = 4 + + +class JLinkRTTDirection(object): + __doc__ = 'RTT Direction.' + UP = 0 + DOWN = 1 +# okay decompiling ./pylink/enums.pyc diff --git a/pylink/errors.py b/pylink/errors.py new file mode 100644 index 0000000..308233a --- /dev/null +++ b/pylink/errors.py @@ -0,0 +1,44 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/errors.py +from . import enums +from . import util + +class JLinkException(enums.JLinkGlobalErrors, Exception): + __doc__ = 'Generic J-Link exception.' + + def __init__(self, code): + message = code + self.code = None + if util.is_integer(code): + message = self.to_string(code) + self.code = code + super(JLinkException, self).__init__(message) + self.message = message + + +class JLinkEraseException(enums.JLinkEraseErrors, JLinkException): + __doc__ = 'J-Link erase exception.' + + +class JLinkFlashException(enums.JLinkFlashErrors, JLinkException): + __doc__ = 'J-Link flash exception.' + + +class JLinkWriteException(enums.JLinkWriteErrors, JLinkException): + __doc__ = 'J-Link write exception.' + + +class JLinkReadException(enums.JLinkReadErrors, JLinkException): + __doc__ = 'J-Link read exception.' + + +class JLinkDataException(enums.JLinkDataErrors, JLinkException): + __doc__ = 'J-Link data event exception.' + + +class JLinkRTTException(enums.JLinkRTTErrors, JLinkException): + __doc__ = 'J-Link RTT exception.' +# okay decompiling ./pylink/errors.pyc diff --git a/pylink/jlink.py b/pylink/jlink.py new file mode 100644 index 0000000..a270edc --- /dev/null +++ b/pylink/jlink.py @@ -0,0 +1,4629 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/jlink.py +from . import binpacker +from . import decorators +from . import enums +from . import errors +from . import jlock +from . import library +from . import structs +from . import unlockers +from . import util +import ctypes, datetime, functools, itertools, logging, math, operator, sys, time, six +logger = logging.getLogger(__name__) + +class JLink(object): + __doc__ = 'Python interface for the SEGGER J-Link.\n\n This is a wrapper around the J-Link C SDK to provide a Python interface\n to it. The shared library is loaded and used to call the SDK methods.\n ' + MAX_BUF_SIZE = 336 + MAX_NUM_CPU_REGISTERS = 256 + MAX_JTAG_SPEED = 12000 + MIN_JTAG_SPEED = 5 + INVALID_JTAG_SPEED = 65534 + AUTO_JTAG_SPEED = 0 + ADAPTIVE_JTAG_SPEED = 65535 + MAX_NUM_MOES = 8 + + def minimum_required(version): + """Decorator to specify the minimum SDK version required. + + Args: + version (str): valid version string + + Returns: + A decorator function. + """ + + def _minimum_required(func): + + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + if list(self.version) < list(version): + raise errors.JLinkException('Version %s required.' % version) + return func(self, *args, **kwargs) + + return wrapper + + return _minimum_required + + def open_required(func): + """Decorator to specify that the J-Link DLL must be opened, and a + J-Link connection must be established. + + Args: + func (function): function being decorated + + Returns: + The wrapper function. + """ + + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + if not self.opened(): + raise errors.JLinkException('J-Link DLL is not open.') + else: + if not self.connected(): + raise errors.JLinkException('J-Link connection has been lost.') + return func(self, *args, **kwargs) + + return wrapper + + def connection_required(func): + """Decorator to specify that a target connection is required in order + for the given method to be used. + + Args: + func (function): function being decorated + + Returns: + The wrapper function. + """ + + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + if not self.target_connected(): + raise errors.JLinkException('Target is not connected.') + return func(self, *args, **kwargs) + + return wrapper + + def interface_required(interface): + """Decorator to specify that a particular interface type is required + for the given method to be used. + + Args: + interface (int): attribute of ``JLinkInterfaces`` + + Returns: + A decorator function. + """ + + def _interface_required(func): + + @functools.wraps(func) + def wrapper(self, *args, **kwargs): + if self.tif != interface: + raise errors.JLinkException('Unsupported for current interface.') + return func(self, *args, **kwargs) + + return wrapper + + return _interface_required + + def __init__(self, lib=None, log=None, detailed_log=None, error=None, warn=None, unsecure_hook=None, serial_no=None, ip_addr=None, open_tunnel=False): + """Initializes the J-Link interface object. + + Note: + By default, the unsecure dialog will reject unsecuring the device on + connection. If you wish to change this behaviour (to have the device + be unsecured and erased), pass a callback that returns + ``JLinkFlags.DLG_BUTTON_YES`` as its return value. + + Args: + self (JLink): the ``JLink`` instance + lib (Library): a valid ``Library`` instance (not ``None`` dll) + log (function): function to be called to write out log messages, by + default this writes to standard out + detailed_log (function): function to be called to write out detailed + log messages, by default this writes to standard out + error (function): function to be called to write out error messages, + default this writes to standard error + warn (function): function to be called to write out warning messages, + default this his writes to standard error + unsecure_hook (function): function to be called for the unsecure + dialog + serial_no (int): serial number of the J-Link + ip_addr (str): IP address and port of the J-Link + (e.g. 192.168.1.1:80) + open_tunnel (bool, None): If ``False`` (default), the ``open`` + method will be called when entering the context manager using + the ``serial_no`` and ``ip_addr`` provided here. + If ``True`` ``open_tunnel`` method will be called instead + of ``open`` method. + If ``None``, the driver will not be opened automatically + (however, it is still closed when exiting the context manager). + + Returns: + ``None`` + + Raises: + TypeError: if lib's DLL is ``None`` + """ + self._initialized = False + if lib is None: + lib = library.Library() + if lib.dll() is None: + raise TypeError('Expected to be given a valid DLL.') + self._library = lib + self._dll = lib.dll() + self._tif = enums.JLinkInterfaces.JTAG + self._unsecure_hook = unsecure_hook or util.unsecure_hook_dialog + self._log_handler = None + self._warning_handler = None + self._error_handler = None + self._detailed_log_handler = None + self._swo_enabled = False + self._lock = None + self._device = None + self._open_refcount = 0 + self._dll.JLINKARM_OpenEx.restype = ctypes.POINTER(ctypes.c_char) + self._dll.JLINKARM_GetCompileDateTime.restype = ctypes.POINTER(ctypes.c_char) + self._dll.JLINKARM_GetRegisterName.restype = ctypes.POINTER(ctypes.c_char) + self.error_handler = lambda s: error or logger.error(s.decode()) + self.warning_handler = lambda s: warn or logger.warning(s.decode()) + self.log_handler = lambda s: log or logger.info(s.decode()) + self.detailed_log_handler = lambda s: detailed_log or logger.debug(s.decode()) + self._JLink__serial_no = serial_no + self._JLink__ip_addr = ip_addr + self._JLink__open_tunnel = open_tunnel + self._initialized = True + + def __del__(self): + """Destructor for the ``JLink`` instance. Closes the J-Link connection + if one exists. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._finalize() + + def __enter__(self): + """Connects to the J-Link emulator (defaults to USB) using context manager. + + Parameters passed to __init__ are used for open() function. + + Returns: + the ``JLink`` instance + + Raises: + JLinkException: if fails to open (i.e. if device is unplugged) + TypeError: if ``serial_no`` is present, but not ``int`` coercible. + AttributeError: if ``serial_no`` and ``ip_addr`` are both ``None``. + """ + if self._JLink__open_tunnel is False: + self.open(serial_no=(self._JLink__serial_no), ip_addr=(self._JLink__ip_addr)) + else: + if self._JLink__open_tunnel is True: + self.open_tunnel(serial_no=(self._JLink__serial_no)) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Closes the JLink connection on exit of the context manager. + + Stops the SWO if enabled and closes the J-Link connection if one + exists. + + Args: + self (JLink): the ``JLink`` instance + exc_type (BaseExceptionType, None): the exception class, if any + raised inside the context manager + exc_val (BaseException, None): the exception object, if any raised + inside the context manager + exc_tb (TracebackType, None): the exception traceback, if any + exception was raised inside the context manager. + + Returns: + ``True`` if exception raised inside the context manager was handled + and shall be suppressed (not propagated), ``None`` otherwise. + """ + self._finalize() + + def _finalize(self): + """Finalizer ("destructor") for the ``JLink`` instance. + + Stops the SWO if enabled and closes the J-Link connection if one + exists. + Called when exiting the context manager or when this object is + destructed (garbage collected). + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + if self._initialized: + if self.connected(): + if self.swo_enabled(): + self.swo_stop() + if self.opened(): + self.close() + + def _get_register_index_from_name(self, register): + """ + Converts a register name to a register index + + Args: + self (JLink): the ``JLink`` instance + register (str): the register name + + Returns: + ``int`` + """ + regs = list((self.register_name(idx) for idx in self.register_list())) + if isinstance(register, six.string_types): + try: + result = regs.index(register) + except ValueError: + error_message = 'No register found matching name: {}. (available registers: {})' + raise errors.JLinkException(error_message.format(register, ', '.join(regs))) + + return result + + def opened(self): + """Returns whether the DLL is open. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the J-Link is open, otherwise ``False``. + """ + return bool(self._dll.JLINKARM_IsOpen()) + + def connected(self): + """Returns whether a J-Link is connected. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the J-Link is open and connected, otherwise ``False``. + """ + return self.opened() and bool(self._dll.JLINKARM_EMU_IsConnected()) + + def target_connected(self): + """Returns whether a target is connected to the J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if a target is connected, otherwise ``False``. + """ + return self.connected() and bool(self._dll.JLINKARM_IsConnected()) + + @property + def log_handler(self): + """Returns the log handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` if the log handler was not set, otherwise a + ``ctypes.CFUNCTYPE``. + """ + return self._log_handler + + @log_handler.setter + def log_handler(self, handler): + """Setter for the log handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + if not self.opened(): + handler = handler or util.noop + self._log_handler = enums.JLinkFunctions.LOG_PROTOTYPE(handler) + self._dll.JLINKARM_EnableLog(self._log_handler) + + @property + def detailed_log_handler(self): + """Returns the detailed log handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` if the detailed log handler was not set, otherwise a + ``ctypes.CFUNCTYPE``. + """ + return self._detailed_log_handler + + @detailed_log_handler.setter + def detailed_log_handler(self, handler): + """Setter for the detailed log handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + if not self.opened(): + handler = handler or util.noop + self._detailed_log_handler = enums.JLinkFunctions.LOG_PROTOTYPE(handler) + self._dll.JLINKARM_EnableLogCom(self._detailed_log_handler) + + @property + def error_handler(self): + """Returns the error handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` if the error handler was not set, otherwise a + ``ctypes.CFUNCTYPE``. + """ + return self._error_handler + + @error_handler.setter + def error_handler(self, handler): + """Setter for the error handler function. + + If the DLL is open, this function is a no-op, so it should be called + prior to calling ``open()``. + + Args: + self (JLink): the ``JLink`` instance + handler (function): function to call on error messages + + Returns: + ``None`` + """ + if not self.opened(): + handler = handler or util.noop + self._error_handler = enums.JLinkFunctions.LOG_PROTOTYPE(handler) + self._dll.JLINKARM_SetErrorOutHandler(self._error_handler) + + @property + def warning_handler(self): + """Returns the warning handler function. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` if the warning handler was not set, otherwise a + ``ctypes.CFUNCTYPE``. + """ + return self._warning_handler + + @warning_handler.setter + def warning_handler(self, handler): + """Setter for the warning handler function. + + If the DLL is open, this function is a no-op, so it should be called + prior to calling ``open()``. + + Args: + self (JLink): the ``JLink`` instance + handler (function): function to call on warning messages + + Returns: + ``None`` + """ + if not self.opened(): + handler = handler or util.noop + self._warning_handler = enums.JLinkFunctions.LOG_PROTOTYPE(handler) + self._dll.JLINKARM_SetWarnOutHandler(self._warning_handler) + + def num_connected_emulators(self): + """Returns the number of emulators which are connected via USB to the + host. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of connected emulators. + """ + return self._dll.JLINKARM_EMU_GetNumDevices() + + def connected_emulators(self, host=enums.JLinkHost.USB): + """Returns a list of all the connected emulators. + + Args: + self (JLink): the ``JLink`` instance + host (int): host type to search (default: ``JLinkHost.USB``) + + Returns: + List of ``JLinkConnectInfo`` specifying the connected emulators. + + Raises: + JLinkException: if fails to enumerate devices. + """ + res = self._dll.JLINKARM_EMU_GetList(host, 0, 0) + if res < 0: + raise errors.JLinkException(res) + num_devices = res + info = (structs.JLinkConnectInfo * num_devices)() + num_found = self._dll.JLINKARM_EMU_GetList(host, info, num_devices) + if num_found < 0: + raise errors.JLinkException(num_found) + return list(info)[:num_found] + + def num_supported_devices(self): + """Returns the number of devices that are supported by the opened + J-Link DLL. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Number of devices the J-Link DLL supports. + """ + return int(self._dll.JLINKARM_DEVICE_GetInfo(-1, 0)) + + def supported_device(self, index=0): + """Gets the device at the given ``index``. + + Args: + self (JLink): the ``JLink`` instance + index (int): the index of the device whose information to get + + Returns: + A ``JLinkDeviceInfo`` describing the requested device. + + Raises: + ValueError: if index is less than 0 or >= supported device count. + """ + if not util.is_natural(index) or index >= self.num_supported_devices(): + raise ValueError('Invalid index.') + info = structs.JLinkDeviceInfo() + result = self._dll.JLINKARM_DEVICE_GetInfo(index, ctypes.byref(info)) + return info + + def open(self, serial_no=None, ip_addr=None): + """Connects to the J-Link emulator (defaults to USB). + + If ``serial_no`` and ``ip_addr`` are both given, this function will + connect to the J-Link over TCP/IP. + + Args: + self (JLink): the ``JLink`` instance + serial_no (int): serial number of the J-Link + ip_addr (str): IP address and port of the J-Link (e.g. 192.168.1.1:80) + + Returns: + ``None`` + + Raises: + JLinkException: if fails to open (i.e. if device is unplugged) + TypeError: if ``serial_no`` is present, but not ``int`` coercible. + AttributeError: if ``serial_no`` and ``ip_addr`` are both ``None``. + """ + if self._open_refcount > 0: + self._open_refcount += 1 + return + self.close() + if ip_addr is not None: + addr, port = ip_addr.rsplit(':', 1) + if serial_no is None: + result = self._dll.JLINKARM_SelectIP(addr.encode(), int(port)) + if result == 1: + raise errors.JLinkException('Could not connect to emulator at %s.' % ip_addr) + else: + self._dll.JLINKARM_EMU_SelectIPBySN(int(serial_no)) + else: + if serial_no is not None: + result = self._dll.JLINKARM_EMU_SelectByUSBSN(int(serial_no)) + if result < 0: + raise errors.JLinkException('No emulator with serial number %s found.' % serial_no) + else: + result = self._dll.JLINKARM_SelectUSB(0) + if result != 0: + raise errors.JlinkException('Could not connect to default emulator.') + if serial_no is not None: + self._lock = jlock.JLock(serial_no) + if not self._lock.acquire(): + raise errors.JLinkException('J-Link is already open.') + result = self._dll.JLINKARM_OpenEx(self.log_handler, self.error_handler) + result = ctypes.cast(result, ctypes.c_char_p).value + if result is not None: + raise errors.JLinkException(result.decode()) + unsecure_hook = self._unsecure_hook + if unsecure_hook is not None: + if hasattr(self._dll, 'JLINK_SetHookUnsecureDialog'): + func = enums.JLinkFunctions.UNSECURE_HOOK_PROTOTYPE(unsecure_hook) + self._dll.JLINK_SetHookUnsecureDialog(func) + self._open_refcount = 1 + + def open_tunnel(self, serial_no, port=19020): + """Connects to the J-Link emulator (over SEGGER tunnel). + + Args: + self (JLink): the ``JLink`` instance + serial_no (int): serial number of the J-Link + port (int): optional port number (default to 19020). + + Returns: + ``None`` + """ + return self.open(ip_addr=('tunnel:' + str(serial_no) + ':' + str(port))) + + def close(self): + """Closes the open J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: if there is no connected JLink. + """ + if self._open_refcount == 0: + return + self._open_refcount -= 1 + if self._open_refcount > 0: + return + self._dll.JLINKARM_Close() + if self._lock is not None: + del self._lock + self._lock = None + + def test(self): + """Performs a self test. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if test passed, otherwise ``False``. + """ + res = self._dll.JLINKARM_Test() + return res == 0 + + @open_required + def invalidate_firmware(self): + """Invalidates the emulator's firmware. + + This method is useful for downgrading the firmware on an emulator. By + calling this method, the current emulator's firmware is invalidated, + which will make the emulator download the firmware of the J-Link SDK + DLL that this instance was created with. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: on hardware error. + """ + self.exec_command('InvalidateFW') + + @open_required + def update_firmware(self): + """Performs a firmware update. + + If there is a newer version of firmware available for the J-Link + device, then updates the firmware. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Checksum of the new firmware on update, ``0`` if the firmware was not + changed. + """ + return self._dll.JLINKARM_UpdateFirmwareIfNewer() + + @open_required + def sync_firmware(self): + """Syncs the emulator's firmware version and the DLL's firmware. + + This method is useful for ensuring that the firmware running on the + J-Link matches the firmware supported by the DLL. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + serial_no = self.serial_number + if self.firmware_newer(): + try: + self.invalidate_firmware() + self.update_firmware() + except errors.JLinkException as e: + try: + pass + finally: + e = None + del e + + res = self.open(serial_no=serial_no) + if self.firmware_newer(): + raise errors.JLinkException('Failed to sync firmware version.') + return res + if self.firmware_outdated(): + try: + self.update_firmware() + except errors.JLinkException as e: + try: + pass + finally: + e = None + del e + + if self.firmware_outdated(): + raise errors.JLinkException('Failed to sync firmware version.') + return self.open(serial_no=serial_no) + + def exec_command(self, cmd): + """Executes the given command. + + This method executes a command by calling the DLL's exec method. + Direct API methods should be prioritized over calling this method. + + Args: + self (JLink): the ``JLink`` instance + cmd (str): the command to run + + Returns: + The return code of running the command. + + Raises: + JLinkException: if the command is invalid or fails. + + See Also: + For a full list of the supported commands, please see the SEGGER + J-Link documentation, + `UM08001 `__. + """ + err_buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + res = self._dll.JLINKARM_ExecCommand(cmd.encode(), err_buf, self.MAX_BUF_SIZE) + err_buf = ctypes.string_at(err_buf).decode() + if len(err_buf) > 0: + raise errors.JLinkException(err_buf.strip()) + return res + + @minimum_required('5.02') + def enable_dialog_boxes(self): + """Enables showing dialog boxes on certain methods. + + Note: + Dialog boxes only appear on Windows platforms. + + Note: + This can be used for batch or automized test running. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self.exec_command('SetBatchMode = 0') + + @minimum_required('5.02') + def disable_dialog_boxes(self): + """Disables showing dialog boxes on certain methods. + + Note: + Dialog boxes only appear on Windows platforms. + + Warning: + This has the effect of also silencing dialog boxes that appear when + updating firmware / to confirm updating firmware. + + Dialog boxes will be shown for a brief period of time (approximately + five seconds), before being automatically hidden, and the default + option chosen. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self.exec_command('SilentUpdateFW') + self.exec_command('SuppressInfoUpdateFW') + self.exec_command('SetBatchMode = 1') + + @open_required + def jtag_configure(self, instr_regs=0, data_bits=0): + """Configures the JTAG scan chain to determine which CPU to address. + + Must be called if the J-Link is connected to a JTAG scan chain with + multiple devices. + + Args: + self (JLink): the ``JLink`` instance + instr_regs (int): length of instruction registers of all devices + closer to TD1 then the addressed CPU + data_bits (int): total number of data bits closer to TD1 than the + addressed CPU + + Returns: + ``None`` + + Raises: + ValueError: if ``instr_regs`` or ``data_bits`` are not natural numbers + """ + if not util.is_natural(instr_regs): + raise ValueError('IR value is not a natural number.') + if not util.is_natural(data_bits): + raise ValueError('Data bits is not a natural number.') + self._dll.JLINKARM_ConfigJTAG(instr_regs, data_bits) + + @open_required + @minimum_required('4.98e') + def coresight_configure(self, ir_pre=0, dr_pre=0, ir_post=0, dr_post=0, ir_len=0, perform_tif_init=True): + """Prepares target and J-Link for CoreSight function usage. + + Args: + self (JLink): the ``JLink`` instance + ir_pre (int): sum of instruction register length of all JTAG devices + in the JTAG chain, close to TDO than the actual one, that J-Link + shall communicate with + dr_pre (int): number of JTAG devices in the JTAG chain, closer to TDO + than the actual one, that J-Link shall communicate with + ir_post (int): sum of instruction register length of all JTAG devices + in the JTAG chain, following the actual one, that J-Link shall + communicate with + dr_post (int): Number of JTAG devices in the JTAG chain, following + the actual one, J-Link shall communicate with + ir_len (int): instruction register length of the actual device that + J-Link shall communicate with + perform_tif_init (bool): if ``False``, then do not output switching + sequence on completion + + Returns: + ``None`` + + Note: + This must be called before calling ``coresight_read()`` or + ``coresight_write()``. + """ + if self.tif == enums.JLinkInterfaces.SWD: + res = self._dll.JLINKARM_CORESIGHT_Configure('') + if res < 0: + raise errors.JLinkException(res) + return + config_string = 'IRPre=%s;DRPre=%s;IRPost=%s;DRPost=%s;IRLenDevice=%s;' + config_string = config_string % (ir_pre, dr_pre, ir_post, dr_post, ir_len) + if not perform_tif_init: + config_string = config_string + 'PerformTIFInit=0;' + res = self._dll.JLINKARM_CORESIGHT_Configure(config_string.encode()) + if res < 0: + raise errors.JLinkException(res) + + @open_required + def connect(self, chip_name, speed='auto', verbose=False): + """Connects the J-Link to its target. + + Args: + self (JLink): the ``JLink`` instance + chip_name (str): target chip name + speed (int): connection speed, one of ``{5-12000, 'auto', 'adaptive'}`` + verbose (bool): boolean indicating if connection should be verbose in logging + + Returns: + ``None`` + + Raises: + JLinkException: if connection fails to establish. + TypeError: if given speed is invalid + """ + if verbose: + self.exec_command('EnableRemarks = 1') + self.exec_command('Device = %s' % chip_name) + if speed == 'auto': + self.set_speed(auto=True) + else: + if speed == 'adaptive': + self.set_speed(adaptive=True) + else: + self.set_speed(speed) + result = self._dll.JLINKARM_Connect() + if result < 0: + raise errors.JLinkException(result) + try: + self.halted() + except errors.JLinkException: + pass + + for index in range(self.num_supported_devices()): + device = self.supported_device(index) + if device.name.lower() == chip_name.lower(): + self._device = device + break + else: + raise errors.JLinkException('Unsupported device was connected to.') + + @property + def error(self): + """DLL internal error state. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The DLL internal error state. This is set if any error occurs in + underlying DLL, otherwise it is ``None``. + """ + error = int(self._dll.JLINKARM_HasError()) + if error == 0: + return + return error + + def clear_error(self): + """Clears the DLL internal error state. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The error state before the clear. + """ + error = self.error + self._dll.JLINKARM_ClrError() + return error + + @property + def compile_date(self): + """Returns a string specifying the date and time at which the DLL was + translated. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Datetime string. + """ + result = self._dll.JLINKARM_GetCompileDateTime() + return ctypes.cast(result, ctypes.c_char_p).value.decode() + + @property + def version(self): + """Returns the device's version. + + The device's version is returned as a string of the format: M.mr where + ``M`` is major number, ``m`` is minor number, and ``r`` is revision + character. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Device version string. + """ + version = int(self._dll.JLINKARM_GetDLLVersion()) + major = version / 10000 + minor = version / 100 % 100 + rev = version % 100 + rev = '' if rev == 0 else chr(rev + ord('a') - 1) + return '%d.%02d%s' % (major, minor, rev) + + @property + @open_required + def compatible_firmware_version(self): + """Returns the DLL's compatible J-Link firmware version. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The firmware version of the J-Link that the DLL is compatible + with. + + Raises: + JLinkException: on error. + """ + identifier = self.firmware_version.split('compiled')[0] + buf_size = self.MAX_BUF_SIZE + buf = (ctypes.c_char * buf_size)() + res = self._dll.JLINKARM_GetEmbeddedFWString(identifier.encode(), buf, buf_size) + if res < 0: + raise errors.JLinkException(res) + return ctypes.string_at(buf).decode() + + @open_required + def firmware_outdated(self): + """Returns whether the J-Link's firmware version is older than the one + that the DLL is compatible with. + + Note: + This is not the same as calling ``not jlink.firmware_newer()``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the J-Link's firmware is older than the one supported by + the DLL, otherwise ``False``. + """ + datefmt = ' %b %d %Y %H:%M:%S' + compat_date = self.compatible_firmware_version.split('compiled')[1] + compat_date = datetime.datetime.strptime(compat_date, datefmt) + fw_date = self.firmware_version.split('compiled')[1] + fw_date = datetime.datetime.strptime(fw_date, datefmt) + return compat_date > fw_date + + @open_required + def firmware_newer(self): + """Returns whether the J-Link's firmware version is newer than the one + that the DLL is compatible with. + + Note: + This is not the same as calling ``not jlink.firmware_outdated()``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the J-Link's firmware is newer than the one supported by + the DLL, otherwise ``False``. + """ + if self.firmware_outdated(): + return False + return self.firmware_version != self.compatible_firmware_version + + @property + @open_required + def hardware_info(self, mask=4294967295): + """Returns a list of 32 integer values corresponding to the bitfields + specifying the power consumption of the target. + + The values returned by this function only have significance if the + J-Link is powering the target. + + The words, indexed, have the following significance: + 0. If ``1``, target is powered via J-Link. + 1. Overcurrent bitfield: + 0: No overcurrent. + 1: Overcurrent happened. 2ms @ 3000mA + 2: Overcurrent happened. 10ms @ 1000mA + 3: Overcurrent happened. 40ms @ 400mA + 2. Power consumption of target (mA). + 3. Peak of target power consumption (mA). + 4. Peak of target power consumption during J-Link operation (mA). + + Args: + self (JLink): the ``JLink`` instance + mask (int): bit mask to decide which hardware information words are + returned (defaults to all the words). + + Returns: + List of bitfields specifying different states based on their index + within the list and their value. + + Raises: + JLinkException: on hardware error. + """ + buf = (ctypes.c_uint32 * 32)() + res = self._dll.JLINKARM_GetHWInfo(mask, ctypes.byref(buf)) + if res != 0: + raise errors.JLinkException(res) + return list(buf) + + @property + @open_required + def hardware_status(self): + """Retrieves and returns the hardware status. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A ``JLinkHardwareStatus`` describing the J-Link hardware. + """ + stat = structs.JLinkHardwareStatus() + res = self._dll.JLINKARM_GetHWStatus(ctypes.byref(stat)) + if res == 1: + raise errors.JLinkException('Error in reading hardware status.') + return stat + + @property + @open_required + def hardware_version(self): + """Returns the hardware version of the connected J-Link as a + major.minor string. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Hardware version string. + """ + version = self._dll.JLINKARM_GetHardwareVersion() + major = version / 10000 % 100 + minor = version / 100 % 100 + return '%d.%02d' % (major, minor) + + @property + @open_required + def firmware_version(self): + """Returns a firmware identification string of the connected J-Link. + + It consists of the following: + - Product Name (e.g. J-Link) + - The string: compiled + - Compile data and time. + - Optional additional information. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Firmware identification string. + """ + buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + self._dll.JLINKARM_GetFirmwareString(buf, self.MAX_BUF_SIZE) + return ctypes.string_at(buf).decode() + + @property + @open_required + def capabilities(self): + """Returns a bitwise combination of the emulator's capabilities. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Bitfield of emulator capabilities. + """ + return self._dll.JLINKARM_GetEmuCaps() + + @property + @open_required + def extended_capabilities(self): + """Gets the capabilities of the connected emulator as a list. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + List of 32 integers which define the extended capabilities based on + their value and index within the list. + """ + buf = (ctypes.c_uint8 * 32)() + self._dll.JLINKARM_GetEmuCapsEx(buf, 32) + return list(buf) + + @open_required + def extended_capability(self, capability): + """Checks if the emulator has the given extended capability. + + Args: + self (JLink): the ``JLink`` instance + capability (int): capability being queried + + Returns: + ``True`` if the emulator has the given extended capability, otherwise + ``False``. + """ + res = self._dll.JLINKARM_EMU_HasCapEx(capability) + return res == 1 + + @property + @open_required + def features(self): + """Returns a list of the J-Link embedded features. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A list of strings, each a feature. Example: + ``[ 'RDI', 'FlashBP', 'FlashDL', 'JFlash', 'GDB' ]`` + """ + buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + self._dll.JLINKARM_GetFeatureString(buf) + result = ctypes.string_at(buf).decode().strip() + if len(result) == 0: + return list() + return result.split(', ') + + @property + @open_required + def product_name(self): + """Returns the product name of the connected J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Product name. + """ + buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + self._dll.JLINKARM_EMU_GetProductName(buf, self.MAX_BUF_SIZE) + return ctypes.string_at(buf).decode() + + @property + @open_required + def serial_number(self): + """Returns the serial number of the connected J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Serial number as an integer. + """ + return self._dll.JLINKARM_GetSN() + + @property + @open_required + def oem(self): + """Retrieves and returns the OEM string of the connected J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The string of the OEM. If this is an original SEGGER product, then + ``None`` is returned instead. + + Raises: + JLinkException: on hardware error. + """ + buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + res = self._dll.JLINKARM_GetOEMString(ctypes.byref(buf)) + if res != 0: + raise errors.JLinkException('Failed to grab OEM string.') + oem = ctypes.string_at(buf).decode() + if len(oem) == 0: + return + return oem + + @property + @open_required + def index(self): + """Retrieves and returns the index number of the actual selected + J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Index of the currently connected J-Link. + """ + return self._dll.JLINKARM_GetSelDevice() + + @property + @open_required + def speed(self): + """Returns the current JTAG connection speed. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + JTAG connection speed. + """ + return self._dll.JLINKARM_GetSpeed() + + @open_required + def set_speed(self, speed=None, auto=False, adaptive=False): + """Sets the speed of the JTAG communication with the ARM core. + + If no arguments are present, automatically detects speed. + + If a ``speed`` is provided, the speed must be no larger than + ``JLink.MAX_JTAG_SPEED`` and no smaller than ``JLink.MIN_JTAG_SPEED``. + The given ``speed`` can also not be ``JLink.INVALID_JTAG_SPEED``. + + Args: + self (JLink): the ``JLink`` instance + speed (int): the speed in kHz to set the communication at + auto (bool): automatically detect correct speed + adaptive (bool): select adaptive clocking as JTAG speed + + Returns: + ``None`` + + Raises: + TypeError: if given speed is not a natural number. + ValueError: if given speed is too high, too low, or invalid. + """ + if speed is None: + speed = 0 + else: + if not util.is_natural(speed): + raise TypeError('Expected positive number for speed, given %s.' % speed) + else: + if speed > self.MAX_JTAG_SPEED: + raise ValueError('Given speed exceeds max speed of %d.' % self.MAX_JTAG_SPEED) + else: + if speed < self.MIN_JTAG_SPEED: + raise ValueError('Given speed is too slow. Minimum is %d.' % self.MIN_JTAG_SPEED) + if auto: + speed = speed | self.AUTO_JTAG_SPEED + if adaptive: + speed = speed | self.ADAPTIVE_JTAG_SPEED + self._dll.JLINKARM_SetSpeed(speed) + + @open_required + def set_max_speed(self): + """Sets JTAG communication speed to the maximum supported speed. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_SetMaxSpeed() + + @property + @open_required + def speed_info(self): + """Retrieves information about supported target interface speeds. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The ``JLinkSpeedInfo`` instance describing the supported target + interface speeds. + """ + speed_info = structs.JLinkSpeedInfo() + self._dll.JLINKARM_GetSpeedInfo(ctypes.byref(speed_info)) + return speed_info + + @property + @open_required + def licenses(self): + """Returns a string of the built-in licenses the J-Link has. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + String of the contents of the built-in licenses the J-Link has. + """ + buf_size = self.MAX_BUF_SIZE + buf = (ctypes.c_char * buf_size)() + res = self._dll.JLINK_GetAvailableLicense(buf, buf_size) + if res < 0: + raise errors.JLinkException(res) + return ctypes.string_at(buf).decode() + + @property + @open_required + @minimum_required('4.98b') + def custom_licenses(self): + """Returns a string of the installed licenses the J-Link has. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + String of the contents of the custom licenses the J-Link has. + """ + buf = (ctypes.c_char * self.MAX_BUF_SIZE)() + result = self._dll.JLINK_EMU_GetLicenses(buf, self.MAX_BUF_SIZE) + if result < 0: + raise errors.JLinkException(result) + return ctypes.string_at(buf).decode() + + @open_required + @minimum_required('4.98b') + def add_license(self, contents): + """Adds the given ``contents`` as a new custom license to the J-Link. + + Args: + self (JLink): the ``JLink`` instance + contents: the string contents of the new custom license + + Returns: + ``True`` if license was added, ``False`` if license already existed. + + Raises: + JLinkException: if the write fails. + + Note: + J-Link V9 and J-Link ULTRA/PRO V4 have 336 Bytes of memory for + licenses, while older versions of 80 bytes. + """ + buf_size = len(contents) + buf = (ctypes.c_char * (buf_size + 1))(*contents.encode()) + res = self._dll.JLINK_EMU_AddLicense(buf) + if res == -1: + raise errors.JLinkException('Unspecified error.') + else: + if res == -2: + raise errors.JLinkException('Failed to read/write license area.') + else: + if res == -3: + raise errors.JLinkException('J-Link out of space.') + return res == 0 + + @open_required + @minimum_required('4.98b') + def erase_licenses(self): + """Erases the custom licenses from the connected J-Link. + + Note: + This method will erase all licenses stored on the J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` on success, otherwise ``False``. + """ + res = self._dll.JLINK_EMU_EraseLicenses() + return res == 0 + + @property + @open_required + def tif(self): + """Returns the current target interface of the J-Link. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Integer specifying the current target interface. + """ + return self._tif + + @open_required + def supported_tifs(self): + """Returns a bitmask of the supported target interfaces. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Bitfield specifying which target interfaces are supported. + """ + buf = ctypes.c_uint32() + self._dll.JLINKARM_TIF_GetAvailable(ctypes.byref(buf)) + return buf.value + + @open_required + def set_tif(self, interface): + """Selects the specified target interface. + + Note that a restart must be triggered for this to take effect. + + Args: + self (Jlink): the ``JLink`` instance + interface (int): integer identifier of the interface + + Returns: + ``True`` if target was updated, otherwise ``False``. + + Raises: + JLinkException: if the given interface is invalid or unsupported. + """ + if not 1 << interface & self.supported_tifs(): + raise errors.JLinkException('Unsupported target interface: %s' % interface) + res = self._dll.JLINKARM_TIF_Select(interface) + if res != 0: + return False + self._tif = interface + return True + + @open_required + def gpio_properties(self): + """Returns the properties of the user-controllable GPIOs. + + Provided the device supports user-controllable GPIOs, they will be + returned by this method. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A list of ``JLinkGPIODescriptor`` instances totalling the number of + requested properties. + + Raises: + JLinkException: on error. + """ + res = self._dll.JLINK_EMU_GPIO_GetProps(0, 0) + if res < 0: + raise errors.JLinkException(res) + num_props = res + buf = (structs.JLinkGPIODescriptor * num_props)() + res = self._dll.JLINK_EMU_GPIO_GetProps(ctypes.byref(buf), num_props) + if res < 0: + raise errors.JLinkException(res) + return list(buf) + + @open_required + def gpio_get(self, pins=None): + """Returns a list of states for the given pins. + + Defaults to the first four pins if an argument is not given. + + Args: + self (JLink): the ``JLink`` instance + pins (list): indices of the GPIO pins whose states are requested + + Returns: + A list of states. + + Raises: + JLinkException: on error. + """ + if pins is None: + pins = range(4) + size = len(pins) + indices = (ctypes.c_uint8 * size)(*pins) + statuses = (ctypes.c_uint8 * size)() + result = self._dll.JLINK_EMU_GPIO_GetState(ctypes.byref(indices), ctypes.byref(statuses), size) + if result < 0: + raise errors.JLinkException(result) + return list(statuses) + + @open_required + def gpio_set(self, pins, states): + """Sets the state for one or more user-controllable GPIOs. + + For each of the given pins, sets the the corresponding state based on + the index. + + Args: + self (JLink): the ``JLink`` instance + pins (list): list of GPIO indices + states (list): list of states to set + + Returns: + A list of updated states. + + Raises: + JLinkException: on error. + ValueError: if ``len(pins) != len(states)`` + """ + if len(pins) != len(states): + raise ValueError('Length mismatch between pins and states.') + size = len(pins) + indices = (ctypes.c_uint8 * size)(*pins) + states = (ctypes.c_uint8 * size)(*states) + result_states = (ctypes.c_uint8 * size)() + result = self._dll.JLINK_EMU_GPIO_SetState(ctypes.byref(indices), ctypes.byref(states), ctypes.byref(result_states), size) + if result < 0: + raise errors.JLinkException(result) + return list(result_states) + + @open_required + def comm_supported(self): + """Returns true if the connected emulator supports ``comm_*`` + functions. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the emulator supports ``comm_*`` functions, otherwise + ``False``. + """ + return bool(self._dll.JLINKARM_EMU_COM_IsSupported()) + + @open_required + def power_on(self, default=False): + """Turns on the power supply over pin 19 of the JTAG connector. + + If given the optional ``default`` parameter, activates the power supply + by default. + + Args: + self (JLink): the ``JLink`` instance + default (bool): boolean indicating if to set power by default + + Returns: + ``None`` + + Raises: + JLinkException: if J-Link does not support powering the target. + """ + if default: + return self.exec_command('SupplyPowerDefault = 1') + return self.exec_command('SupplyPower = 1') + + @open_required + def power_off(self, default=False): + """Turns off the power supply over pin 19 of the JTAG connector. + + If given the optional ``default`` parameter, deactivates the power supply + by default. + + Args: + self (JLink): the ``JLink`` instance + default (bool): boolean indicating if to set power off by default + + Returns: + The current ``JLink`` instance + + Raises: + JLinkException: if J-Link does not support powering the target. + """ + if default: + return self.exec_command('SupplyPowerDefault = 0') + return self.exec_command('SupplyPower = 0') + + @connection_required + def unlock(self): + """Unlocks the device connected to the J-Link. + + Unlocking a device allows for access to read/writing memory, as well as + flash programming. + + Note: + Unlock is not supported on all devices. + + Supported Devices: + Kinetis + + Returns: + ``True``. + + Raises: + JLinkException: if the device fails to unlock. + """ + if not unlockers.unlock(self, self._device.manufacturer): + raise errors.JLinkException('Failed to unlock device.') + return True + + @connection_required + def cpu_capability(self, capability): + """Checks whether the J-Link has support for a CPU capability. + + This method checks if the emulator has built-in intelligence to handle + the given CPU capability for the target CPU it is connected to. + + Args: + self (JLink): the ``JLink`` instance + capability (int): the capability to check for + + Returns: + ``True`` if the J-Link has built-in intelligence to support the given + ``capability`` for the CPU it is connected to, otherwise ``False``. + """ + res = self._dll.JLINKARM_EMU_HasCPUCap(capability) + return res == 1 + + @connection_required + def set_trace_source(self, source): + """Sets the source to be used for tracing. + + The ``source`` must be one of the ones provided by + ``enums.JLinkTraceSource``. + + Args: + self (JLink): the ``JLink`` instance. + source (int): the source to use. + + Returns: + ``None`` + """ + self._dll.JLINKARM_SelectTraceSource(source) + + @connection_required + def set_etb_trace(self): + """Sets the trace source to ETB. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + """ + return self.set_trace_source(enums.JLinkTraceSource.ETB) + + @connection_required + def set_etm_trace(self): + """Sets the trace source to ETM. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + """ + return self.set_trace_source(enums.JLinkTraceSource.ETM) + + @connection_required + def set_reset_strategy(self, strategy): + """Sets the reset strategy for the target. + + The reset strategy defines what happens when the target is reset. + + Args: + self (JLink): the ``JLink`` instance + strategy (int): the reset strategy to use + + Returns: + The previous reset streategy. + """ + return self._dll.JLINKARM_SetResetType(strategy) + + @connection_required + def set_reset_pin_high(self): + """Sets the reset pin high. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_SetRESET() + + @connection_required + def set_reset_pin_low(self): + """Sets the reset pin low. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ClrRESET() + + @connection_required + def set_tck_pin_high(self): + """Sets the TCK pin to the high value (1). + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: if the emulator does not support this feature. + """ + res = self._dll.JLINKARM_SetTCK() + if res < 0: + raise errors.JLinkException('Feature not supported.') + + @connection_required + def set_tck_pin_low(self): + """Sets the TCK pin to the low value (0). + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: if the emulator does not support this feature. + """ + res = self._dll.JLINKARM_ClrTCK() + if res < 0: + raise errors.JLinkException('Feature not supported.') + + @connection_required + def set_tdi_pin_high(self): + """Sets the test data input to logical ``1``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_SetTDI() + + @connection_required + def set_tdi_pin_low(self): + """Clears the test data input. + + TDI is set to logical ``0`` (Ground). + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ClrTDI() + + @connection_required + def set_tms_pin_high(self): + """Sets the test mode select to logical ``1``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_SetTMS() + + @connection_required + def set_tms_pin_low(self): + """Clears the test mode select. + + TMS is set to logical ``0`` (Ground). + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ClrTMS() + + @connection_required + def set_trst_pin_high(self): + """Sets the TRST pin to high (``1``). + + Deasserts the TRST pin. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_SetTRST() + + @connection_required + def set_trst_pin_low(self): + """Sets the TRST pin to low (``0``). + + This asserts the TRST pin. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ClrTRST() + + @connection_required + def erase(self): + """Erases the flash contents of the device. + + This erases the flash memory of the target device. If this method + fails, the device may be left in an inoperable state. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Number of bytes erased. + """ + try: + if not self.halted(): + self.halt() + except errors.JLinkException: + pass + + res = self._dll.JLINK_EraseChip() + if res < 0: + raise errors.JLinkEraseException(res) + return res + + @connection_required + def flash(self, data, addr, on_progress=None, power_on=False, flags=0): + """Flashes the target device. + + The given ``on_progress`` callback will be called as + ``on_progress(action, progress_string, percentage)`` periodically as the + data is written to flash. The action is one of ``Compare``, ``Erase``, + ``Verify``, ``Flash``. + + Args: + self (JLink): the ``JLink`` instance + data (list): list of bytes to write to flash + addr (int): start address on flash which to write the data + on_progress (function): callback to be triggered on flash progress + power_on (boolean): whether to power the target before flashing + flags (int): reserved, do not use + + Returns: + Number of bytes flashed. This number may not necessarily be equal to + ``len(data)``, but that does not indicate an error. + + Raises: + JLinkException: on hardware errors. + """ + if flags != 0: + raise errors.JLinkException('Flags are reserved for future use.') + if on_progress is not None: + func = enums.JLinkFunctions.FLASH_PROGRESS_PROTOTYPE(on_progress) + self._dll.JLINK_SetFlashProgProgressCallback(func) + else: + self._dll.JLINK_SetFlashProgProgressCallback(0) + if power_on: + self.power_on() + try: + if not self.halted(): + self.halt() + except errors.JLinkException: + pass + + res = self.flash_write(addr, data, flags=flags) + return res + + @connection_required + def flash_file(self, path, addr, on_progress=None, power_on=False): + """Flashes the target device. + + The given ``on_progress`` callback will be called as + ``on_progress(action, progress_string, percentage)`` periodically as the + data is written to flash. The action is one of ``Compare``, ``Erase``, + ``Verify``, ``Flash``. + + Args: + self (JLink): the ``JLink`` instance + path (str): absolute path to the source file to flash + addr (int): start address on flash which to write the data + on_progress (function): callback to be triggered on flash progress + power_on (boolean): whether to power the target before flashing + + Returns: + Integer value greater than or equal to zero. Has no significance. + + Raises: + JLinkException: on hardware errors. + """ + if on_progress is not None: + func = enums.JLinkFunctions.FLASH_PROGRESS_PROTOTYPE(on_progress) + self._dll.JLINK_SetFlashProgProgressCallback(func) + else: + self._dll.JLINK_SetFlashProgProgressCallback(0) + if power_on: + self.power_on() + try: + if not self.halted(): + self.halt() + except errors.JLinkException: + pass + + bytes_flashed = self._dll.JLINK_DownloadFile(path.encode(), addr) + if bytes_flashed < 0: + raise errors.JLinkFlashException(bytes_flashed) + return bytes_flashed + + @connection_required + def reset(self, ms=0, halt=True): + """Resets the target. + + This method resets the target, and by default toggles the RESET and + TRST pins. + + Args: + self (JLink): the ``JLink`` instance + ms (int): Amount of milliseconds to delay after reset (default: 0) + halt (bool): if the CPU should halt after reset (default: True) + + Returns: + Number of bytes read. + """ + self._dll.JLINKARM_SetResetDelay(ms) + res = self._dll.JLINKARM_Reset() + if res < 0: + raise errors.JLinkException(res) + else: + if not halt: + self._dll.JLINKARM_Go() + return res + + @connection_required + def reset_tap(self): + """Resets the TAP controller via TRST. + + Note: + This must be called at least once after power up if the TAP + controller is to be used. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ResetTRST() + + @connection_required + def restart(self, num_instructions=0, skip_breakpoints=False): + """Restarts the CPU core and simulates/emulates instructions. + + Note: + This is a no-op if the CPU isn't halted. + + Args: + self (JLink): the ``JLink`` instance + num_instructions (int): number of instructions to simulate, defaults + to zero + skip_breakpoints (bool): skip current breakpoint (default: ``False``) + + Returns: + ``True`` if device was restarted, otherwise ``False``. + + Raises: + ValueError: if instruction count is not a natural number. + """ + if not util.is_natural(num_instructions): + raise ValueError('Invalid instruction count: %s.' % num_instructions) + if not self.halted(): + return False + flags = 0 + if skip_breakpoints: + flags = flags | enums.JLinkFlags.GO_OVERSTEP_BP + self._dll.JLINKARM_GoEx(num_instructions, flags) + return True + + @connection_required + @decorators.async_decorator + def halt(self): + """Halts the CPU Core. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if halted, ``False`` otherwise. + """ + res = int(self._dll.JLINKARM_Halt()) + if res == 0: + time.sleep(1) + return True + return False + + @connection_required + def halted(self): + """Returns whether the CPU core was halted. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if the CPU core is halted, otherwise ``False``. + + Raises: + JLinkException: on device errors. + """ + result = int(self._dll.JLINKARM_IsHalted()) + if result < 0: + raise errors.JLinkException(result) + return result > 0 + + @connection_required + def core_id(self): + """Returns the identifier of the target ARM core. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Integer identifier of ARM core. + """ + return self._dll.JLINKARM_GetId() + + @connection_required + def core_cpu(self): + """Returns the identifier of the core CPU. + + Note: + This is distinct from the value returned from ``core_id()`` which is + the ARM specific identifier. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The identifier of the CPU core. + """ + return self._dll.JLINKARM_CORE_GetFound() + + @connection_required + def core_name(self): + """Returns the name of the target ARM core. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The target core's name. + """ + buf_size = self.MAX_BUF_SIZE + buf = (ctypes.c_char * buf_size)() + self._dll.JLINKARM_Core2CoreName(self.core_cpu(), buf, buf_size) + return ctypes.string_at(buf).decode() + + @connection_required + def ir_len(self): + """Counts and returns the total length of instruction registers of all + the devices in the JTAG scan chain. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Total instruction register length. + """ + return self._dll.JLINKARM_GetIRLen() + + @connection_required + def scan_len(self): + """Retrieves and returns the length of the scan chain select register. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Length of the scan chain select register. + """ + return self._dll.JLINKARM_GetScanLen() + + @connection_required + def scan_chain_len(self, scan_chain): + """Retrieves and returns the number of bits in the scan chain. + + Args: + self (JLink): the ``JLink`` instance + scan_chain (int): scan chain to be measured + + Returns: + Number of bits in the specified scan chain. + + Raises: + JLinkException: on error. + """ + res = self._dll.JLINKARM_MeasureSCLen(scan_chain) + if res < 0: + raise errors.JLinkException(res) + return res + + @connection_required + def device_family(self): + """Returns the device family of the target CPU. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Integer identifier of the device family. + """ + return self._dll.JLINKARM_GetDeviceFamily() + + @connection_required + def register_list(self): + """Returns a list of the indices for the CPU registers. + + The returned indices can be used to read the register content or grab + the register name. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + List of registers. + """ + num_items = self.MAX_NUM_CPU_REGISTERS + buf = (ctypes.c_uint32 * num_items)() + num_regs = self._dll.JLINKARM_GetRegisterList(buf, num_items) + return buf[:num_regs] + + @connection_required + def register_name(self, register_index): + """Retrives and returns the name of an ARM CPU register. + + Args: + self (JLink): the ``JLink`` instance + register_index (int): index of the register whose name to retrieve + + Returns: + Name of the register. + """ + result = self._dll.JLINKARM_GetRegisterName(register_index) + return ctypes.cast(result, ctypes.c_char_p).value.decode() + + @connection_required + def cpu_speed(self, silent=False): + """Retrieves the CPU speed of the target. + + If the target does not support CPU frequency detection, this function + will return ``0``. + + Args: + self (JLink): the ``JLink`` instance + silent (bool): ``True`` if the CPU detection should not report errors + to the error handler on failure. + + Returns: + The measured CPU frequency on success, otherwise ``0`` if the core does + not support CPU frequency detection. + + Raises: + JLinkException: on hardware error. + """ + res = self._dll.JLINKARM_MeasureCPUSpeedEx(-1, 1, int(silent)) + if res < 0: + raise errors.JLinkException(res) + return res + + @connection_required + def cpu_halt_reasons(self): + """Retrives the reasons that the CPU was halted. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A list of ``JLInkMOEInfo`` instances specifying the reasons for which + the CPU was halted. This list may be empty in the case that the CPU + is not halted. + + Raises: + JLinkException: on hardware error. + """ + buf_size = self.MAX_NUM_MOES + buf = (structs.JLinkMOEInfo * buf_size)() + num_reasons = self._dll.JLINKARM_GetMOEs(buf, buf_size) + if num_reasons < 0: + raise errors.JLinkException(num_reasons) + return list(buf)[:num_reasons] + + @connection_required + def jtag_create_clock(self): + """Creates a JTAG clock on TCK. + + Note: + This function only needs to be called once. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The state of the TDO pin: either ``0`` or ``1``. + """ + return self._dll.JLINKARM_Clock() + + @connection_required + def jtag_send(self, tms, tdi, num_bits): + """Sends data via JTAG. + + Sends data via JTAG on the rising clock edge, TCK. At on each rising + clock edge, on bit is transferred in from TDI and out to TDO. The + clock uses the TMS to step through the standard JTAG state machine. + + Args: + self (JLink): the ``JLink`` instance + tms (int): used to determine the state transitions for the Test + Access Port (TAP) controller from its current state + tdi (int): input data to be transferred in from TDI to TDO + num_bits (int): a number in the range ``[1, 32]`` inclusively + specifying the number of meaningful bits in the ``tms`` and + ``tdi`` parameters for the purpose of extracting state and data + information + + Returns: + ``None`` + + Raises: + ValueError: if ``num_bits < 1`` or ``num_bits > 32``. + + See Also: + `JTAG Technical Overview `_. + """ + if util.is_natural(num_bits) and num_bits <= 0 or num_bits > 32: + raise ValueError('Number of bits must be >= 1 and <= 32.') + self._dll.JLINKARM_StoreBits(tms, tdi, num_bits) + + @connection_required + def jtag_flush(self): + """Flushes the internal JTAG buffer. + + Note: + The buffer is automatically flushed when a response from the target + is expected, or the buffer is full. This can be used after a + ``memory_write()`` in order to flush the buffer. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_WriteBits() + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_read8(self, offset): + """Gets a unit of ``8`` bits from the input buffer. + + Args: + self (JLink): the ``JLink`` instance + offset (int): the offset (in bits) from which to start reading + + Returns: + The integer read from the input buffer. + """ + value = self._dll.JLINK_SWD_GetU8(offset) + return ctypes.c_uint8(value).value + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_read16(self, offset): + """Gets a unit of ``16`` bits from the input buffer. + + Args: + self (JLink): the ``JLink`` instance + offset (int): the offset (in bits) from which to start reading + + Returns: + The integer read from the input buffer. + """ + value = self._dll.JLINK_SWD_GetU16(offset) + return ctypes.c_uint16(value).value + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_read32(self, offset): + """Gets a unit of ``32`` bits from the input buffer. + + Args: + self (JLink): the ``JLink`` instance + offset (int): the offset (in bits) from which to start reading + + Returns: + The integer read from the input buffer. + """ + value = self._dll.JLINK_SWD_GetU32(offset) + return ctypes.c_uint32(value).value + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_write(self, output, value, nbits): + """Writes bytes over SWD (Serial Wire Debug). + + Args: + self (JLink): the ``JLink`` instance + output (int): the output buffer offset to write to + value (int): the value to write to the output buffer + nbits (int): the number of bits needed to represent the ``output`` and + ``value`` + + Returns: + The bit position of the response in the input buffer. + """ + pDir = binpacker.pack(output, nbits) + pIn = binpacker.pack(value, nbits) + bitpos = self._dll.JLINK_SWD_StoreRaw(pDir, pIn, nbits) + if bitpos < 0: + raise errors.JLinkException(bitpos) + return bitpos + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_write8(self, output, value): + """Writes one byte over SWD (Serial Wire Debug). + + Args: + self (JLink): the ``JLink`` instance + output (int): the output buffer offset to write to + value (int): the value to write to the output buffer + + Returns: + The bit position of the response in the input buffer. + """ + return self.swd_write(output, value, 8) + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_write16(self, output, value): + """Writes two bytes over SWD (Serial Wire Debug). + + Args: + self (JLink): the ``JLink`` instance + output (int): the output buffer offset to write to + value (int): the value to write to the output buffer + + Returns: + The bit position of the response in the input buffer. + """ + return self.swd_write(output, value, 16) + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_write32(self, output, value): + """Writes four bytes over SWD (Serial Wire Debug). + + Args: + self (JLink): the ``JLink`` instance + output (int): the output buffer offset to write to + value (int): the value to write to the output buffer + + Returns: + The bit position of the response in the input buffer. + """ + return self.swd_write(output, value, 32) + + @interface_required(enums.JLinkInterfaces.SWD) + @connection_required + def swd_sync(self, pad=False): + """Causes a flush to write all data remaining in output buffers to SWD + device. + + Args: + self (JLink): the ``JLink`` instance + pad (bool): ``True`` if should pad the data to full byte size + + Returns: + ``None`` + """ + if pad: + self._dll.JLINK_SWD_SyncBytes() + else: + self._dll.JLINK_SWD_SyncBits() + + @connection_required + def flash_write(self, addr, data, nbits=None, flags=0): + """Writes data to the flash region of a device. + + The given number of bits, if provided, must be either ``8``, ``16``, or + ``32``. + + Args: + self (JLink): the ``JLink`` instance + addr (int): starting flash address to write to + data (list): list of data units to write + nbits (int): number of bits to use for each unit + + Returns: + Number of bytes written to flash. + """ + self._dll.JLINKARM_BeginDownload(flags) + self.memory_write(addr, data, nbits=nbits) + bytes_flashed = self._dll.JLINKARM_EndDownload() + if bytes_flashed < 0: + raise errors.JLinkFlashException(bytes_flashed) + return bytes_flashed + + @connection_required + def flash_write8(self, addr, data): + """Writes bytes to the flash region of a device. + + Args: + self (JLink): the ``JLink`` instance + addr (int): starting flash address to write to + data (list): list of bytes to write + + Returns: + Number of bytes written to flash. + """ + return self.flash_write(addr, data, 8) + + @connection_required + def flash_write16(self, addr, data): + """Writes halfwords to the flash region of a device. + + Args: + self (JLink): the ``JLink`` instance + addr (int): starting flash address to write to + data (list): list of halfwords to write + + Returns: + Number of bytes written to flash. + """ + return self.flash_write(addr, data, 16) + + @connection_required + def flash_write32(self, addr, data): + """Writes words to the flash region of a device. + + Args: + self (JLink): the ``JLink`` instance + addr (int): starting flash address to write to + data (list): list of words to write + + Returns: + Number of bytes written to flash. + """ + return self.flash_write(addr, data, 32) + + @connection_required + def code_memory_read(self, addr, num_bytes): + """Reads bytes from code memory. + + Note: + This is similar to calling ``memory_read`` or ``memory_read8``, + except that this uses a cache and reads ahead. This should be used + in instances where you want to read a small amount of bytes at a + time, and expect to always read ahead. + + Args: + self (JLink): the ``JLink`` instance + addr (int): starting address from which to read + num_bytes (int): number of bytes to read + + Returns: + A list of bytes read from the target. + + Raises: + JLinkException: if memory could not be read. + """ + buf_size = num_bytes + buf = (ctypes.c_uint8 * buf_size)() + res = self._dll.JLINKARM_ReadCodeMem(addr, buf_size, buf) + if res < 0: + raise errors.JLinkException(res) + return list(buf)[:res] + + @connection_required + def num_memory_zones(self): + """Returns the number of memory zones supported by the target. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + An integer count of the number of memory zones supported by the + target. + + Raises: + JLinkException: on error. + """ + count = self._dll.JLINK_GetMemZones(0, 0) + if count < 0: + raise errors.JLinkException(count) + return count + + @connection_required + def memory_zones(self): + """Gets all memory zones supported by the current target. + + Some targets support multiple memory zones. This function provides the + ability to get a list of all the memory zones to facilate using the + memory zone routing functions. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A list of all the memory zones as ``JLinkMemoryZone`` structures. + + Raises: + JLinkException: on hardware errors. + """ + count = self.num_memory_zones() + if count == 0: + return list() + buf = (structs.JLinkMemoryZone * count)() + res = self._dll.JLINK_GetMemZones(buf, count) + if res < 0: + raise errors.JLinkException(res) + return list(buf) + + @connection_required + def memory_read(self, addr, num_units, zone=None, nbits=None): + """Reads memory from a target system or specific memory zone. + + The optional ``zone`` specifies a memory zone to access to read from, + e.g. ``IDATA``, ``DDATA``, or ``CODE``. + + The given number of bits, if provided, must be either ``8``, ``16``, or + ``32``. If not provided, always reads ``num_units`` bytes. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to read from + num_units (int): number of units to read + zone (str): optional memory zone name to access + nbits (int): number of bits to use for each unit + + Returns: + List of units read from the target system. + + Raises: + JLinkException: if memory could not be read. + ValueError: if ``nbits`` is not ``None``, and not in ``8``, ``16``, + or ``32``. + """ + buf_size = num_units + buf = None + access = 0 + if nbits is None: + buf = (ctypes.c_uint8 * buf_size)() + access = 0 + else: + if nbits == 8: + buf = (ctypes.c_uint8 * buf_size)() + access = 1 + else: + if nbits == 16: + buf = (ctypes.c_uint16 * buf_size)() + access = 2 + buf_size = buf_size * access + else: + if nbits == 32: + buf = (ctypes.c_uint32 * buf_size)() + access = 4 + buf_size = buf_size * access + else: + raise ValueError('Given bit size is invalid: %s' % nbits) + args = [ + addr, buf_size, buf, access] + method = self._dll.JLINKARM_ReadMemEx + if zone is not None: + method = self._dll.JLINKARM_ReadMemZonedEx + args.append(zone.encode()) + units_read = method(*args) + if units_read < 0: + raise errors.JLinkReadException(units_read) + return buf[:units_read] + + @connection_required + def memory_read8(self, addr, num_bytes, zone=None): + """Reads memory from the target system in units of bytes. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to read from + num_bytes (int): number of bytes to read + zone (str): memory zone to read from + + Returns: + List of bytes read from the target system. + + Raises: + JLinkException: if memory could not be read. + """ + return self.memory_read(addr, num_bytes, zone=zone, nbits=8) + + @connection_required + def memory_read16(self, addr, num_halfwords, zone=None): + """Reads memory from the target system in units of 16-bits. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to read from + num_halfwords (int): number of half words to read + zone (str): memory zone to read from + + Returns: + List of halfwords read from the target system. + + Raises: + JLinkException: if memory could not be read + """ + return self.memory_read(addr, num_halfwords, zone=zone, nbits=16) + + @connection_required + def memory_read32(self, addr, num_words, zone=None): + """Reads memory from the target system in units of 32-bits. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to read from + num_words (int): number of words to read + zone (str): memory zone to read from + + Returns: + List of words read from the target system. + + Raises: + JLinkException: if memory could not be read + """ + return self.memory_read(addr, num_words, zone=zone, nbits=32) + + @connection_required + def memory_read64(self, addr, num_long_words): + """Reads memory from the target system in units of 64-bits. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to read from + num_long_words (int): number of long words to read + + Returns: + List of long words read from the target system. + + Raises: + JLinkException: if memory could not be read + """ + buf_size = num_long_words + buf = (ctypes.c_ulonglong * buf_size)() + units_read = self._dll.JLINKARM_ReadMemU64(addr, buf_size, buf, 0) + if units_read < 0: + raise errors.JLinkException(units_read) + return buf[:units_read] + + @connection_required + def memory_write(self, addr, data, zone=None, nbits=None): + """Writes memory to a target system or specific memory zone. + + The optional ``zone`` specifies a memory zone to access to write to, + e.g. ``IDATA``, ``DDATA``, or ``CODE``. + + The given number of bits, if provided, must be either ``8``, ``16``, or + ``32``. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to write to + data (list): list of data units to write + zone (str): optional memory zone name to access + nbits (int): number of bits to use for each unit + + Returns: + Number of units written. + + Raises: + JLinkException: on write hardware failure. + ValueError: if ``nbits`` is not ``None``, and not in ``8``, ``16`` or + ``32``. + """ + buf_size = len(data) + buf = None + access = 0 + if nbits is None: + packed_data = map(lambda d: reversed(binpacker.pack(d)) +, data) + packed_data = list((itertools.chain)(*packed_data)) + buf_size = len(packed_data) + buf = (ctypes.c_uint8 * buf_size)(*packed_data) + access = 0 + else: + if nbits == 8: + buf = (ctypes.c_uint8 * buf_size)(*data) + access = 1 + else: + if nbits == 16: + buf = (ctypes.c_uint16 * buf_size)(*data) + access = 2 + buf_size = buf_size * access + else: + if nbits == 32: + buf = (ctypes.c_uint32 * buf_size)(*data) + access = 4 + buf_size = buf_size * access + else: + raise ValueError('Given bit size is invalid: %s' % nbits) + args = [ + addr, buf_size, buf, access] + method = self._dll.JLINKARM_WriteMemEx + if zone is not None: + method = self._dll.JLINKARM_WriteMemZonedEx + args.append(zone.encode()) + units_written = method(*args) + if units_written < 0: + raise errors.JLinkWriteException(units_written) + return units_written + + @connection_required + def memory_write8(self, addr, data, zone=None): + """Writes bytes to memory of a target system. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to write to + data (list): list of bytes to write + zone (str): optional memory zone to access + + Returns: + Number of bytes written to target. + + Raises: + JLinkException: on memory access error. + """ + return self.memory_write(addr, data, zone, 8) + + @connection_required + def memory_write16(self, addr, data, zone=None): + """Writes half-words to memory of a target system. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to write to + data (list): list of half-words to write + zone (str): optional memory zone to access + + Returns: + Number of half-words written to target. + + Raises: + JLinkException: on memory access error. + """ + return self.memory_write(addr, data, zone, 16) + + @connection_required + def memory_write32(self, addr, data, zone=None): + """Writes words to memory of a target system. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to write to + data (list): list of words to write + zone (str): optional memory zone to access + + Returns: + Number of words written to target. + + Raises: + JLinkException: on memory access error. + """ + return self.memory_write(addr, data, zone, 32) + + @connection_required + def memory_write64(self, addr, data, zone=None): + """Writes long words to memory of a target system. + + Note: + This is little-endian. + + Args: + self (JLink): the ``JLink`` instance + addr (int): start address to write to + data (list): list of long words to write + zone (str): optional memory zone to access + + Returns: + Number of long words written to target. + + Raises: + JLinkException: on memory access error. + """ + words = [] + bitmask = 4294967295 + for long_word in data: + words.append(long_word & bitmask) + words.append(long_word >> 32 & bitmask) + + return self.memory_write32(addr, words, zone=zone) + + @connection_required + def register_read(self, register_index): + """Reads the value from the given register. + + Args: + self (JLink): the ``JLink`` instance + register_index (int/str): the register to read + + Returns: + The value stored in the given register. + """ + if isinstance(register_index, six.string_types): + register_index = self._get_register_index_from_name(register_index) + return self._dll.JLINKARM_ReadReg(register_index) + + @connection_required + def register_read_multiple(self, register_indices): + """Retrieves the values from the registers specified. + + Args: + self (JLink): the ``JLink`` instance + register_indices (list): list of registers to read + + Returns: + A list of values corresponding one-to-one for each of the given + register indices. The returned list of values are the values in + order of which the indices were specified. + + Raises: + JLinkException: if a given register is invalid or an error occurs. + """ + register_indices = register_indices[:] + num_regs = len(register_indices) + for idx, indice in enumerate(register_indices): + if isinstance(indice, six.string_types): + register_indices[idx] = self._get_register_index_from_name(indice) + + buf = (ctypes.c_uint32 * num_regs)(*register_indices) + data = ctypes.c_uint32 * num_regs(0) + statuses = ctypes.c_uint8 * num_regs(0) + res = self._dll.JLINKARM_ReadRegs(buf, data, statuses, num_regs) + if res < 0: + raise errors.JLinkException(res) + return list(data) + + @connection_required + def register_write(self, reg_index, value): + """Writes into an ARM register. + + Note: + The data is not immediately written, but is cached before being + transferred to the CPU on CPU start. + + Args: + self (JLink): the ``JLink`` instance + reg_index (int/str): the ARM register to write to + value (int): the value to write to the register + + Returns: + The value written to the ARM register. + + Raises: + JLinkException: on write error. + """ + if isinstance(reg_index, six.string_types): + reg_index = self._get_register_index_from_name(reg_index) + res = self._dll.JLINKARM_WriteReg(reg_index, value) + if res != 0: + raise errors.JLinkException('Error writing to register %d' % reg_index) + return value + + @connection_required + def register_write_multiple(self, register_indices, values): + """Writes to multiple CPU registers. + + Writes the values to the given registers in order. There must be a + one-to-one correspondence between the values and the registers + specified. + + Args: + self (JLink): the ``JLink`` instance + register_indices (list): list of registers to write to + values (list): list of values to write to the registers + + Returns: + ``None`` + + Raises: + ValueError: if ``len(register_indices) != len(values)`` + JLinkException: if a register could not be written to or on error + """ + register_indices = register_indices[:] + if len(register_indices) != len(values): + raise ValueError('Must be an equal number of registers and values') + num_regs = len(register_indices) + for idx, indice in enumerate(register_indices): + if isinstance(indice, six.string_types): + register_indices[idx] = self._get_register_index_from_name(indice) + + buf = (ctypes.c_uint32 * num_regs)(*register_indices) + data = (ctypes.c_uint32 * num_regs)(*values) + statuses = ctypes.c_uint8 * num_regs(0) + res = self._dll.JLINKARM_WriteRegs(buf, data, statuses, num_regs) + if res != 0: + raise errors.JLinkException(res) + + @connection_required + def ice_register_read(self, register_index): + """Reads a value from an ARM ICE register. + + Args: + self (JLink): the ``JLink`` instance + register_index (int): the register to read + + Returns: + The value read from the register. + """ + return self._dll.JLINKARM_ReadICEReg(register_index) + + @connection_required + def ice_register_write(self, register_index, value, delay=False): + """Writes a value to an ARM ICE register. + + Args: + self (JLink): the ``JLink`` instance + register_index (int): the ICE register to write to + value (int): the value to write to the ICE register + delay (bool): boolean specifying if the write should be delayed + + Returns: + ``None`` + """ + self._dll.JLINKARM_WriteICEReg(register_index, int(value), int(delay)) + + @connection_required + def etm_supported(self): + """Returns if the CPU core supports ETM. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``True`` if the CPU has the ETM unit, otherwise ``False``. + """ + res = self._dll.JLINKARM_ETM_IsPresent() + if res == 1: + return True + info = ctypes.c_uint32(0) + index = enums.JLinkROMTable.ETM + res = self._dll.JLINKARM_GetDebugInfo(index, ctypes.byref(info)) + if res == 1: + return False + return True + + @connection_required + def etm_register_read(self, register_index): + """Reads a value from an ETM register. + + Args: + self (JLink): the ``JLink`` instance. + register_index (int): the register to read. + + Returns: + The value read from the ETM register. + """ + return self._dll.JLINKARM_ETM_ReadReg(register_index) + + @connection_required + def etm_register_write(self, register_index, value, delay=False): + """Writes a value to an ETM register. + + Args: + self (JLink): the ``JLink`` instance. + register_index (int): the register to write to. + value (int): the value to write to the register. + delay (bool): boolean specifying if the write should be buffered. + + Returns: + ``None`` + """ + self._dll.JLINKARM_ETM_WriteReg(int(register_index), int(value), int(delay)) + + @connection_required + def coresight_read(self, reg, ap=True): + """Reads an Ap/DP register on a CoreSight DAP. + + Wait responses and special handling are both handled by this method. + + Note: + ``coresight_configure()`` must be called prior to calling this method. + + Args: + self (JLink): the ``JLink`` instance + reg (int): index of DP/AP register to read + ap (bool): ``True`` if reading from an Access Port register, + otherwise ``False`` for Debug Port + + Returns: + Data read from register. + + Raises: + JLinkException: on hardware error + """ + data = ctypes.c_uint32() + ap = 1 if ap else 0 + res = self._dll.JLINKARM_CORESIGHT_ReadAPDPReg(reg, ap, ctypes.byref(data)) + if res < 0: + raise errors.JLinkException(res) + return data.value + + @connection_required + def coresight_write(self, reg, data, ap=True): + """Writes an Ap/DP register on a CoreSight DAP. + + Note: + ``coresight_configure()`` must be called prior to calling this method. + + Args: + self (JLink): the ``JLink`` instance + reg (int): index of DP/AP register to write + data (int): data to write + ap (bool): ``True`` if writing to an Access Port register, otherwise + ``False`` for Debug Port + + Returns: + Number of repetitions needed until write request accepted. + + Raises: + JLinkException: on hardware error + """ + ap = 1 if ap else 0 + res = self._dll.JLINKARM_CORESIGHT_WriteAPDPReg(reg, ap, data) + if res < 0: + raise errors.JLinkException(res) + return res + + @connection_required + def enable_reset_pulls_reset(self): + """Enables RESET pin toggling on the JTAG bus on resets. + + When ``.reset()`` is called, it will also toggle the RESET pin on the + JTAG bus. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ResetPullsRESET(1) + + @connection_required + def disable_reset_pulls_reset(self): + """Disables RESET pin toggling on the JTAG bus on resets. + + When ``.reset()`` is called, it will not toggle the RESET pin on the + JTAG bus. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ResetPullsRESET(0) + + @connection_required + def enable_reset_pulls_trst(self): + """Enables TRST pin toggling on the JTAG bus on resets. + + When ``.reset()`` is called, it will also toggle the TRST pin on the + JTAG bus. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ResetPullsTRST(1) + + @connection_required + def disable_reset_pulls_trst(self): + """Disables TRST pin toggling on the JTAG bus on resets. + + When ``.reset()`` is called, it will not toggle the TRST pin on the + JTAG bus. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_ResetPullsTRST(0) + + @connection_required + def enable_reset_inits_registers(self): + """Enables CPU register initialization on resets. + + When ``.reset()`` is called, it will initialize the CPU registers. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if was previously enabled, otherwise ``False``. + """ + return bool(self._dll.JLINKARM_SetInitRegsOnReset(1)) + + @connection_required + def disable_reset_inits_registers(self): + """Disables CPU register initialization on resets. + + When ``.reset()`` is called, the CPU registers will be read and not + initialized. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if was previously enabled, otherwise ``False``. + """ + return bool(self._dll.JLINKARM_SetInitRegsOnReset(0)) + + @connection_required + def set_little_endian(self): + """Sets the target hardware to little endian. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if target was big endian before call, otherwise ``False``. + """ + res = self._dll.JLINKARM_SetEndian(0) + return res == 1 + + @connection_required + def set_big_endian(self): + """Sets the target hardware to big endian. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if target was little endian before call, otherwise ``False``. + """ + res = self._dll.JLINKARM_SetEndian(1) + return res == 0 + + @connection_required + def set_vector_catch(self, flags): + """Sets vector catch bits of the processor. + + The CPU will jump to a vector if the given vector catch is active, and + will enter a debug state. This has the effect of halting the CPU as + well, meaning the CPU must be explicitly restarted. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + res = self._dll.JLINKARM_WriteVectorCatch(flags) + if res < 0: + raise errors.JLinkException(res) + + @connection_required + def step(self, thumb=False): + """Executes a single step. + + Steps even if there is a breakpoint. + + Args: + self (JLink): the ``JLink`` instance + thumb (bool): boolean indicating if to step in thumb mode + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + method = self._dll.JLINKARM_Step + if thumb: + method = self._dll.JLINKARM_StepComposite + res = method() + if res != 0: + raise errors.JLinkException('Failed to step over instruction.') + + @connection_required + def enable_soft_breakpoints(self): + """Enables software breakpoints. + + Note: + This should be called before calling ``software_breakpoint_set()``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_EnableSoftBPs(1) + + @connection_required + def disable_soft_breakpoints(self): + """Disables software breakpoints. + + Note: + After this function is called, ``software_breakpoint_set()`` cannot + be used without first calling ``enable_soft_breakpoints()``. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + """ + self._dll.JLINKARM_EnableSoftBPs(0) + + @connection_required + def num_active_breakpoints(self): + """Returns the number of currently active breakpoints. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of breakpoints that are currently set. + """ + return self._dll.JLINKARM_GetNumBPs() + + @connection_required + def num_available_breakpoints(self, arm=False, thumb=False, ram=False, flash=False, hw=False): + """Returns the number of available breakpoints of the specified type. + + If ``arm`` is set, gets the number of available ARM breakpoint units. + If ``thumb`` is set, gets the number of available THUMB breakpoint + units. If ``ram`` is set, gets the number of available software RAM + breakpoint units. If ``flash`` is set, gets the number of available + software flash breakpoint units. If ``hw`` is set, gets the number of + available hardware breakpoint units. + + If a combination of the flags is given, then + ``num_available_breakpoints()`` returns the number of breakpoints + specified by the given flags. If no flags are specified, then the + count of available breakpoint units is returned. + + Args: + self (JLink): the ``JLink`` instance + arm (bool): Boolean indicating to get number of ARM breakpoints. + thumb (bool): Boolean indicating to get number of THUMB breakpoints. + ram (bool): Boolean indicating to get number of SW RAM breakpoints. + flash (bool): Boolean indicating to get number of Flash breakpoints. + hw (bool): Boolean indicating to get number of Hardware breakpoints. + + Returns: + The number of available breakpoint units of the specified type. + """ + flags = [ + enums.JLinkBreakpoint.ARM, + enums.JLinkBreakpoint.THUMB, + enums.JLinkBreakpoint.SW_RAM, + enums.JLinkBreakpoint.SW_FLASH, + enums.JLinkBreakpoint.HW] + set_flags = [ + arm, + thumb, + ram, + flash, + hw] + if not any(set_flags): + flags = enums.JLinkBreakpoint.ANY + else: + flags = list((f for i, f in enumerate(flags) if set_flags[i])) + flags = functools.reduce(operator.__or__, flags, 0) + return self._dll.JLINKARM_GetNumBPUnits(flags) + + @connection_required + def breakpoint_info(self, handle=0, index=-1): + """Returns the information about a set breakpoint. + + Note: + Either ``handle`` or ``index`` can be specified. If the ``index`` + is not provided, the ``handle`` must be set, and vice-versa. If + both ``index`` and ``handle`` are provided, the ``index`` overrides + the provided ``handle``. + + Args: + self (JLink): the ``JLink`` instance + handle (int): option handle of a valid breakpoint + index (int): optional index of the breakpoint. + + Returns: + An instance of ``JLinkBreakpointInfo`` specifying information about + the breakpoint. + + Raises: + JLinkException: on error. + ValueError: if both the handle and index are invalid. + """ + if index < 0: + if handle == 0: + raise ValueError('Handle must be provided if index is not set.') + bp = structs.JLinkBreakpointInfo() + bp.Handle = int(handle) + res = self._dll.JLINKARM_GetBPInfoEx(index, ctypes.byref(bp)) + if res < 0: + raise errors.JLinkException('Failed to get breakpoint info.') + return bp + + @connection_required + def breakpoint_find(self, addr): + """Returns the handle of a breakpoint at the given address, if any. + + Args: + self (JLink): the ``JLink`` instance + addr (int): the address to search for the breakpoint + + Returns: + A non-zero integer if a breakpoint was found at the given address, + otherwise zero. + """ + return self._dll.JLINKARM_FindBP(addr) + + @connection_required + def breakpoint_set(self, addr, thumb=False, arm=False): + """Sets a breakpoint at the specified address. + + If ``thumb`` is ``True``, the breakpoint is set in THUMB-mode, while if + ``arm`` is ``True``, the breakpoint is set in ARM-mode, otherwise a + normal breakpoint is set. + + Args: + self (JLink): the ``JLink`` instance + addr (int): the address where the breakpoint will be set + thumb (bool): boolean indicating to set the breakpoint in THUMB mode + arm (bool): boolean indicating to set the breakpoint in ARM mode + + Returns: + An integer specifying the breakpoint handle. This handle should be + retained for future breakpoint operations. + + Raises: + TypeError: if the given address is not an integer. + JLinkException: if the breakpoint could not be set. + """ + flags = enums.JLinkBreakpoint.ANY + if thumb: + flags = flags | enums.JLinkBreakpoint.THUMB + else: + if arm: + flags = flags | enums.JLinkBreakpoint.ARM + handle = self._dll.JLINKARM_SetBPEx(int(addr), flags) + if handle <= 0: + raise errors.JLinkException('Breakpoint could not be set.') + return handle + + @connection_required + def software_breakpoint_set(self, addr, thumb=False, arm=False, flash=False, ram=False): + """Sets a software breakpoint at the specified address. + + If ``thumb`` is ``True``, the breakpoint is set in THUMB-mode, while if + ``arm`` is ``True``, the breakpoint is set in ARM-mode, otherwise a + normal breakpoint is set. + + If ``flash`` is ``True``, the breakpoint is set in flash, otherwise if + ``ram`` is ``True``, the breakpoint is set in RAM. If both are + ``True`` or both are ``False``, then the best option is chosen for + setting the breakpoint in software. + + Args: + self (JLink): the ``JLink`` instance + addr (int): the address where the breakpoint will be set + thumb (bool): boolean indicating to set the breakpoint in THUMB mode + arm (bool): boolean indicating to set the breakpoint in ARM mode + flash (bool): boolean indicating to set the breakpoint in flash + ram (bool): boolean indicating to set the breakpoint in RAM + + Returns: + An integer specifying the breakpoint handle. This handle should sbe + retained for future breakpoint operations. + + Raises: + TypeError: if the given address is not an integer. + JLinkException: if the breakpoint could not be set. + """ + if flash and not ram: + flags = enums.JLinkBreakpoint.SW_FLASH + else: + if not flash or ram: + flags = enums.JLinkBreakpoint.SW_RAM + else: + flags = enums.JLinkBreakpoint.SW + if thumb: + flags = flags | enums.JLinkBreakpoint.THUMB + else: + if arm: + flags = flags | enums.JLinkBreakpoint.ARM + handle = self._dll.JLINKARM_SetBPEx(int(addr), flags) + if handle <= 0: + raise errors.JLinkException('Software breakpoint could not be set.') + return handle + + @connection_required + def hardware_breakpoint_set(self, addr, thumb=False, arm=False): + """Sets a hardware breakpoint at the specified address. + + If ``thumb`` is ``True``, the breakpoint is set in THUMB-mode, while if + ``arm`` is ``True``, the breakpoint is set in ARM-mode, otherwise a + normal breakpoint is set. + + Args: + self (JLink): the ``JLink`` instance + addr (int): the address where the breakpoint will be set + thumb (bool): boolean indicating to set the breakpoint in THUMB mode + arm (bool): boolean indicating to set the breakpoint in ARM mode + + Returns: + An integer specifying the breakpoint handle. This handle should sbe + retained for future breakpoint operations. + + Raises: + TypeError: if the given address is not an integer. + JLinkException: if the breakpoint could not be set. + """ + flags = enums.JLinkBreakpoint.HW + if thumb: + flags = flags | enums.JLinkBreakpoint.THUMB + else: + if arm: + flags = flags | enums.JLinkBreakpoint.ARM + handle = self._dll.JLINKARM_SetBPEx(int(addr), flags) + if handle <= 0: + raise errors.JLinkException('Hardware breakpoint could not be set.') + return handle + + @connection_required + def breakpoint_clear(self, handle): + """Removes a single breakpoint. + + Args: + self (JLink): the ``JLink`` instance + handle (int): the handle of the breakpoint to be removed + + Returns: + ``True`` if the breakpoint was cleared, otherwise ``False`` if the + breakpoint was not valid. + """ + return not self._dll.JLINKARM_ClrBPEx(handle) + + @connection_required + def breakpoint_clear_all(self): + """Removes all breakpoints that have been set. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if they were cleared, otherwise ``False``. + """ + return not self._dll.JLINKARM_ClrBPEx(4294967295) + + @connection_required + def num_active_watchpoints(self): + """Returns the number of currently active watchpoints. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of watchpoints that are currently set. + """ + return self._dll.JLINKARM_GetNumWPs() + + @connection_required + def num_available_watchpoints(self): + """Returns the number of available watchpoints. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of watchpoints that are available to be set. + """ + return self._dll.JLINKARM_GetNumWPUnits() + + @connection_required + def watchpoint_info(self, handle=0, index=-1): + """Returns information about the specified watchpoint. + + Note: + Either ``handle`` or ``index`` can be specified. If the ``index`` + is not provided, the ``handle`` must be set, and vice-versa. If + both ``index`` and ``handle`` are provided, the ``index`` overrides + the provided ``handle``. + + Args: + self (JLink): the ``JLink`` instance + handle (int): optional handle of a valid watchpoint. + index (int): optional index of a watchpoint. + + Returns: + An instance of ``JLinkWatchpointInfo`` specifying information about + the watchpoint if the watchpoint was found, otherwise ``None``. + + Raises: + JLinkException: on error. + ValueError: if both handle and index are invalid. + """ + if index < 0: + if handle == 0: + raise ValueError('Handle must be provided if index is not set.') + wp = structs.JLinkWatchpointInfo() + res = self._dll.JLINKARM_GetWPInfoEx(index, ctypes.byref(wp)) + if res < 0: + raise errors.JLinkException('Failed to get watchpoint info.') + for i in range(res): + res = self._dll.JLINKARM_GetWPInfoEx(i, ctypes.byref(wp)) + if res < 0: + raise errors.JLinkException('Failed to get watchpoint info.') + if wp.Handle == handle or wp.WPUnit == index: + return wp + + @connection_required + def watchpoint_set(self, addr, addr_mask=0, data=0, data_mask=0, access_size=None, read=False, write=False, privileged=False): + """Sets a watchpoint at the given address. + + This method allows for a watchpoint to be set on an given address or + range of addresses. The watchpoint can then be triggered if the data + at the given address matches the specified ``data`` or range of data as + determined by ``data_mask``, on specific access size events, reads, + writes, or privileged accesses. + + Both ``addr_mask`` and ``data_mask`` are used to specify ranges. Bits + set to ``1`` are masked out and not taken into consideration when + comparison against an address or data value. E.g. an ``addr_mask`` + with a value of ``0x1`` and ``addr`` with value ``0xdeadbeef`` means + that the watchpoint will be set on addresses ``0xdeadbeef`` and + ``0xdeadbeee``. If the ``data`` was ``0x11223340`` and the given + ``data_mask`` has a value of ``0x0000000F``, then the watchpoint would + trigger for data matching ``0x11223340 - 0x1122334F``. + + Note: + If both ``read`` and ``write`` are specified, then the watchpoint + will trigger on both read and write events to the given address. + + Args: + self (JLink): the ``JLink`` instance + addr_mask (int): optional mask to use for determining which address + the watchpoint should be set on + data (int): optional data to set the watchpoint on in order to have + the watchpoint triggered when the value at the specified address + matches the given ``data`` + data_mask (int): optional mask to use for determining the range of + data on which the watchpoint should be triggered + access_size (int): if specified, this must be one of ``{8, 16, 32}`` + and determines the access size for which the watchpoint should + trigger + read (bool): if ``True``, triggers the watchpoint on read events + write (bool): if ``True``, triggers the watchpoint on write events + privileged (bool): if ``True``, triggers the watchpoint on privileged + accesses + + Returns: + The handle of the created watchpoint. + + Raises: + ValueError: if an invalid access size is given. + JLinkException: if the watchpoint fails to be set. + """ + access_flags = 0 + access_mask_flags = 0 + if access_size is None: + access_mask_flags = access_mask_flags | enums.JLinkAccessMaskFlags.SIZE + else: + if access_size == 8: + access_flags = access_flags | enums.JLinkAccessFlags.SIZE_8BIT + else: + if access_size == 16: + access_flags = access_flags | enums.JLinkAccessFlags.SIZE_16BIT + else: + if access_size == 32: + access_flags = access_flags | enums.JLinkAccessFlags.SIZE_32BIT + else: + raise ValueError('Invalid access size given: %d' % access_size) + if read and write: + access_mask_flags = access_mask_flags | enums.JLinkAccessMaskFlags.DIR + else: + if read: + access_flags = access_flags | enums.JLinkAccessFlags.READ + else: + if write: + access_flags = access_flags | enums.JLinkAccessFlags.WRITE + if privileged: + access_flags = access_flags | enums.JLinkAccessFlags.PRIV + else: + access_mask_flags = access_mask_flags | enums.JLinkAccessMaskFlags.PRIV + wp = structs.JLinkDataEvent() + wp.Addr = addr + wp.AddrMask = addr_mask + wp.Data = data + wp.DataMask = data_mask + wp.Access = access_flags + wp.AccessMask = access_mask_flags + handle = ctypes.c_uint32() + res = self._dll.JLINKARM_SetDataEvent(ctypes.pointer(wp), ctypes.pointer(handle)) + if res < 0: + raise errors.JLinkDataException(res) + return handle.value + + @connection_required + def watchpoint_clear(self, handle): + """Clears the watchpoint with the specified handle. + + Args: + self (JLink): the ``JLink`` instance + handle (int): the handle of the watchpoint + + Returns: + ``True`` if watchpoint was removed, otherwise ``False``. + """ + return not self._dll.JLINKARM_ClrDataEvent(handle) + + @connection_required + def watchpoint_clear_all(self): + """Removes all watchpoints that have been set. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if they were cleared, otherwise ``False``. + """ + return not self._dll.JLINKARM_ClrDataEvent(4294967295) + + def disassemble_instruction(self, instruction): + """Disassembles and returns the assembly instruction string. + + Args: + self (JLink): the ``JLink`` instance. + instruction (int): the instruction address. + + Returns: + A string corresponding to the assembly instruction string at the + given instruction address. + + Raises: + JLinkException: on error. + TypeError: if ``instruction`` is not a number. + """ + if not util.is_integer(instruction): + raise TypeError('Expected instruction to be an integer.') + buf_size = self.MAX_BUF_SIZE + buf = (ctypes.c_char * buf_size)() + res = self._dll.JLINKARM_DisassembleInst(ctypes.byref(buf), buf_size, instruction) + if res < 0: + raise errors.JLinkException('Failed to disassemble instruction.') + return ctypes.string_at(buf).decode() + + @connection_required + def strace_configure(self, port_width): + """Configures the trace port width for tracing. + + Note that configuration cannot occur while STRACE is running. + + Args: + self (JLink): the ``JLink`` instance + port_width (int): the trace port width to use. + + Returns: + ``None`` + + Raises: + ValueError: if ``port_width`` is not ``1``, ``2``, or ``4``. + JLinkException: on error. + """ + if port_width not in (1, 2, 4): + raise ValueError('Invalid port width: %s' % str(port_width)) + config_string = 'PortWidth=%d' % port_width + res = self._dll.JLINK_STRACE_Config(config_string.encode()) + if res < 0: + raise errors.JLinkException('Failed to configure STRACE port') + + @connection_required + def strace_start(self): + """Starts the capturing of STRACE data. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + res = self._dll.JLINK_STRACE_Start() + if res < 0: + raise errors.JLinkException('Failed to start STRACE.') + + @connection_required + def strace_stop(self): + """Stops the sampling of STRACE data. + + Any capturing of STRACE data is automatically stopped when the CPU is + halted. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + res = self._dll.JLINK_STRACE_Stop() + if res < 0: + raise errors.JLinkException('Failed to stop STRACE.') + + @connection_required + def strace_read(self, num_instructions): + """Reads and returns a number of instructions captured by STRACE. + + The number of instructions must be a non-negative value of at most + ``0x10000`` (``65536``). + + Args: + self (JLink): the ``JLink`` instance. + num_instructions (int): number of instructions to fetch. + + Returns: + A list of instruction addresses in order from most recently executed + to oldest executed instructions. Note that the number of + instructions returned can be less than the number of instructions + requested in the case that there are not ``num_instructions`` in the + trace buffer. + + Raises: + JLinkException: on error. + ValueError: if ``num_instructions < 0`` or + ``num_instructions > 0x10000``. + """ + if num_instructions < 0 or num_instructions > 65536: + raise ValueError('Invalid instruction count.') + buf = (ctypes.c_uint32 * num_instructions)() + buf_size = num_instructions + res = self._dll.JLINK_STRACE_Read(ctypes.byref(buf), buf_size) + if res < 0: + raise errors.JLinkException('Failed to read from STRACE buffer.') + return list(buf)[:res] + + @connection_required + def strace_code_fetch_event(self, operation, address, address_range=0): + """Sets an event to trigger trace logic when an instruction is fetched. + + Args: + self (JLink): the ``JLink`` instance. + operation (int): one of the operations in ``JLinkStraceOperation``. + address (int): the address of the instruction that is fetched. + address_range (int): optional range of address to trigger event on. + + Returns: + An integer specifying the trace event handle. This handle should be + retained in order to clear the event at a later time. + + Raises: + JLinkException: on error. + """ + cmd = enums.JLinkStraceCommand.TRACE_EVENT_SET + event_info = structs.JLinkStraceEventInfo() + event_info.Type = enums.JLinkStraceEvent.CODE_FETCH + event_info.Op = operation + event_info.Addr = int(address) + event_info.AddrRangeSize = int(address_range) + handle = self._dll.JLINK_STRACE_Control(cmd, ctypes.byref(event_info)) + if handle < 0: + raise errors.JLinkException(handle) + return handle + + @connection_required + def strace_data_access_event(self, operation, address, data, data_mask=None, access_width=4, address_range=0): + """Sets an event to trigger trace logic when data access is made. + + Data access corresponds to either a read or write. + + Args: + self (JLink): the ``JLink`` instance. + operation (int): one of the operations in ``JLinkStraceOperation``. + address (int): the address of the load/store data. + data (int): the data to be compared the event data to. + data_mask (int): optional bitmask specifying bits to ignore in + comparison. + acess_width (int): optional access width for the data. + address_range (int): optional range of address to trigger event on. + + Returns: + An integer specifying the trace event handle. This handle should be + retained in order to clear the event at a later time. + + Raises: + JLinkException: on error. + """ + cmd = enums.JLinkStraceCommand.TRACE_EVENT_SET + event_info = structs.JLinkStraceEventInfo() + event_info.Type = enums.JLinkStraceEvent.DATA_ACCESS + event_info.Op = operation + event_info.AccessSize = int(access_width) + event_info.Addr = int(address) + event_info.Data = int(data) + event_info.DataMask = int(data_mask or 0) + event_info.AddrRangeSize = int(address_range) + handle = self._dll.JLINK_STRACE_Control(cmd, ctypes.byref(event_info)) + if handle < 0: + raise errors.JLinkException(handle) + return handle + + @connection_required + def strace_data_load_event(self, operation, address, address_range=0): + """Sets an event to trigger trace logic when data read access is made. + + Args: + self (JLink): the ``JLink`` instance. + operation (int): one of the operations in ``JLinkStraceOperation``. + address (int): the address of the load data. + address_range (int): optional range of address to trigger event on. + + Returns: + An integer specifying the trace event handle. This handle should be + retained in order to clear the event at a later time. + + Raises: + JLinkException: on error. + """ + cmd = enums.JLinkStraceCommand.TRACE_EVENT_SET + event_info = structs.JLinkStraceEventInfo() + event_info.Type = enums.JLinkStraceEvent.DATA_LOAD + event_info.Op = operation + event_info.Addr = int(address) + event_info.AddrRangeSize = int(address_range) + handle = self._dll.JLINK_STRACE_Control(cmd, ctypes.byref(event_info)) + if handle < 0: + raise errors.JLinkException(handle) + return handle + + @connection_required + def strace_data_store_event(self, operation, address, address_range=0): + """Sets an event to trigger trace logic when data write access is made. + + Args: + self (JLink): the ``JLink`` instance. + operation (int): one of the operations in ``JLinkStraceOperation``. + address (int): the address of the store data. + address_range (int): optional range of address to trigger event on. + + Returns: + An integer specifying the trace event handle. This handle should be + retained in order to clear the event at a later time. + + Raises: + JLinkException: on error. + """ + cmd = enums.JLinkStraceCommand.TRACE_EVENT_SET + event_info = structs.JLinkStraceEventInfo() + event_info.Type = enums.JLinkStraceEvent.DATA_STORE + event_info.Op = operation + event_info.Addr = int(address) + event_info.AddrRangeSize = int(address_range) + handle = self._dll.JLINK_STRACE_Control(cmd, ctypes.byref(event_info)) + if handle < 0: + raise errors.JLinkException(handle) + return handle + + @connection_required + def strace_clear(self, handle): + """Clears the trace event specified by the given handle. + + Args: + self (JLink): the ``JLink`` instance. + handle (int): handle of the trace event. + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + data = ctypes.c_int(handle) + res = self._dll.JLINK_STRACE_Control(enums.JLinkStraceCommand.TRACE_EVENT_CLR, ctypes.byref(data)) + if res < 0: + raise errors.JLinkException('Failed to clear STRACE event.') + + @connection_required + def strace_clear_all(self): + """Clears all STRACE events. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + data = 0 + res = self._dll.JLINK_STRACE_Control(enums.JLinkStraceCommand.TRACE_EVENT_CLR_ALL, data) + if res < 0: + raise errors.JLinkException('Failed to clear all STRACE events.') + + @connection_required + def strace_set_buffer_size(self, size): + """Sets the STRACE buffer size. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + + Raises: + JLinkException: on error. + """ + size = ctypes.c_uint32(size) + res = self._dll.JLINK_STRACE_Control(enums.JLinkStraceCommand.SET_BUFFER_SIZE, size) + if res < 0: + raise errors.JLinkException('Failed to set the STRACE buffer size.') + + @connection_required + def trace_start(self): + """Starts collecting trace data. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + """ + cmd = enums.JLinkTraceCommand.START + res = self._dll.JLINKARM_TRACE_Control(cmd, 0) + if res == 1: + raise errors.JLinkException('Failed to start trace.') + + @connection_required + def trace_stop(self): + """Stops collecting trace data. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + """ + cmd = enums.JLinkTraceCommand.STOP + res = self._dll.JLINKARM_TRACE_Control(cmd, 0) + if res == 1: + raise errors.JLinkException('Failed to stop trace.') + + @connection_required + def trace_flush(self): + """Flushes the trace buffer. + + After this method is called, the trace buffer is empty. This method is + best called when the device is reset. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + ``None`` + """ + cmd = enums.JLinkTraceCommand.FLUSH + res = self._dll.JLINKARM_TRACE_Control(cmd, 0) + if res == 1: + raise errors.JLinkException('Failed to flush the trace buffer.') + + @connection_required + def trace_sample_count(self): + """Retrieves the number of samples in the trace buffer. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + Number of samples in the trace buffer. + """ + cmd = enums.JLinkTraceCommand.GET_NUM_SAMPLES + data = ctypes.c_uint32(self.trace_max_buffer_capacity()) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get trace sample count.') + return data.value + + @connection_required + def trace_buffer_capacity(self): + """Retrieves the trace buffer's current capacity. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + The current capacity of the trace buffer. This is not necessarily + the maximum possible size the buffer could be configured with. + """ + cmd = enums.JLinkTraceCommand.GET_CONF_CAPACITY + data = ctypes.c_uint32(0) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get trace buffer size.') + return data.value + + @connection_required + def trace_set_buffer_capacity(self, size): + """Sets the capacity for the trace buffer. + + Args: + self (JLink): the ``JLink`` instance. + size (int): the new capacity for the trace buffer. + + Returns: + ``None`` + """ + cmd = enums.JLinkTraceCommand.SET_CAPACITY + data = ctypes.c_uint32(size) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to set trace buffer size.') + + @connection_required + def trace_min_buffer_capacity(self): + """Retrieves the minimum capacity the trace buffer can be configured with. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + The minimum configurable capacity for the trace buffer. + """ + cmd = enums.JLinkTraceCommand.GET_MIN_CAPACITY + data = ctypes.c_uint32(0) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get min trace buffer size.') + return data.value + + @connection_required + def trace_max_buffer_capacity(self): + """Retrieves the maximum size the trace buffer can be configured with. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + The maximum configurable capacity for the trace buffer. + """ + cmd = enums.JLinkTraceCommand.GET_MAX_CAPACITY + data = ctypes.c_uint32(0) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get max trace buffer size.') + return data.value + + @connection_required + def trace_set_format(self, fmt): + """Sets the format for the trace buffer to use. + + Args: + self (JLink): the ``JLink`` instance. + fmt (int): format for the trace buffer; this is one of the attributes + of ``JLinkTraceFormat``. + + Returns: + ``None`` + """ + cmd = enums.JLinkTraceCommand.SET_FORMAT + data = ctypes.c_uint32(fmt) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to set trace format.') + + @connection_required + def trace_format(self): + """Retrieves the current format the trace buffer is using. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + The current format the trace buffer is using. This is one of the + attributes of ``JLinkTraceFormat``. + """ + cmd = enums.JLinkTraceCommand.GET_FORMAT + data = ctypes.c_uint32(0) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get trace format.') + return data.value + + @connection_required + def trace_region_count(self): + """Retrieves a count of the number of available trace regions. + + Args: + self (JLink): the ``JLink`` instance. + + Returns: + Count of the number of available trace regions. + """ + cmd = enums.JLinkTraceCommand.GET_NUM_REGIONS + data = ctypes.c_uint32(0) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(data)) + if res == 1: + raise errors.JLinkException('Failed to get trace region count.') + return data.value + + @connection_required + def trace_region(self, region_index): + """Retrieves the properties of a trace region. + + Args: + self (JLink): the ``JLink`` instance. + region_index (int): the trace region index. + + Returns: + An instance of ``JLinkTraceRegion`` describing the specified region. + """ + cmd = enums.JLinkTraceCommand.GET_REGION_PROPS_EX + region = structs.JLinkTraceRegion() + region.RegionIndex = int(region_index) + res = self._dll.JLINKARM_TRACE_Control(cmd, ctypes.byref(region)) + if res == 1: + raise errors.JLinkException('Failed to get trace region.') + return region + + @connection_required + def trace_read(self, offset, num_items): + """Reads data from the trace buffer and returns it. + + Args: + self (JLink): the ``JLink`` instance. + offset (int): the offset from which to start reading from the trace + buffer. + num_items (int): number of items to read from the trace buffer. + + Returns: + A list of ``JLinkTraceData`` instances corresponding to the items + read from the trace buffer. Note that this list may have size less + than ``num_items`` in the event that there are not ``num_items`` + items in the trace buffer. + + Raises: + JLinkException: on error. + """ + buf_size = ctypes.c_uint32(num_items) + buf = (structs.JLinkTraceData * num_items)() + res = self._dll.JLINKARM_TRACE_Read(buf, int(offset), ctypes.byref(buf_size)) + if res == 1: + raise errors.JLinkException('Failed to read from trace buffer.') + return list(buf)[:int(buf_size.value)] + + def swo_enabled(self): + """Returns whether or not SWO is enabled. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``True`` if SWO is enabled, otherwise ``False``. + """ + return self._swo_enabled + + @connection_required + def swo_start(self, swo_speed=9600): + """Starts collecting SWO data. + + Note: + If SWO is already enabled, it will first stop SWO before enabling it + again. + + Args: + self (JLink): the ``JLink`` instance + swo_speed (int): the frequency in Hz used by the target to communicate + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + if self.swo_enabled(): + self.swo_stop() + info = structs.JLinkSWOStartInfo() + info.Speed = swo_speed + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.START, ctypes.byref(info)) + if res < 0: + raise errors.JLinkException(res) + self._swo_enabled = True + + @connection_required + def swo_stop(self): + """Stops collecting SWO data. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.STOP, 0) + if res < 0: + raise errors.JLinkException(res) + + @connection_required + def swo_enable(self, cpu_speed, swo_speed=9600, port_mask=1): + """Enables SWO output on the target device. + + Configures the output protocol, the SWO output speed, and enables any + ITM & stimulus ports. + + This is equivalent to calling ``.swo_start()``. + + Note: + If SWO is already enabled, it will first stop SWO before enabling it + again. + + Args: + self (JLink): the ``JLink`` instance + cpu_speed (int): the target CPU frequency in Hz + swo_speed (int): the frequency in Hz used by the target to communicate + port_mask (int): port mask specifying which stimulus ports to enable + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + if self.swo_enabled(): + self.swo_stop() + res = self._dll.JLINKARM_SWO_EnableTarget(cpu_speed, swo_speed, enums.JLinkSWOInterfaces.UART, port_mask) + if res != 0: + raise errors.JLinkException(res) + self._swo_enabled = True + + @connection_required + def swo_disable(self, port_mask): + """Disables ITM & Stimulus ports. + + Args: + self (JLink): the ``JLink`` instance + port_mask (int): mask specifying which ports to disable + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + res = self._dll.JLINKARM_SWO_DisableTarget(port_mask) + if res != 0: + raise errors.JLinkException(res) + + @connection_required + def swo_flush(self, num_bytes=None): + """Flushes data from the SWO buffer. + + After this method is called, the flushed part of the SWO buffer is + empty. + + If ``num_bytes`` is not present, flushes all data currently in the SWO + buffer. + + Args: + self (JLink): the ``JLink`` instance + num_bytes (int): the number of bytes to flush + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + if num_bytes is None: + num_bytes = self.swo_num_bytes() + buf = ctypes.c_uint32(num_bytes) + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.FLUSH, ctypes.byref(buf)) + if res < 0: + raise errors.JLinkException(res) + + @connection_required + def swo_speed_info(self): + """Retrieves information about the supported SWO speeds. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + A ``JLinkSWOSpeedInfo`` instance describing the target's supported + SWO speeds. + + Raises: + JLinkException: on error + """ + info = structs.JLinkSWOSpeedInfo() + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.GET_SPEED_INFO, ctypes.byref(info)) + if res < 0: + raise errors.JLinkException(res) + return info + + @connection_required + def swo_num_bytes(self): + """Retrives the number of bytes in the SWO buffer. + + Args: + self (JLink): the ``JLink`` instance + + Returns: + Number of bytes in the SWO buffer. + + Raises: + JLinkException: on error + """ + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.GET_NUM_BYTES, 0) + if res < 0: + raise errors.JLinkException(res) + return res + + @connection_required + def swo_set_host_buffer_size(self, buf_size): + """Sets the size of the buffer used by the host to collect SWO data. + + Args: + self (JLink): the ``JLink`` instance + buf_size (int): the new size of the host buffer + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + buf = ctypes.c_uint32(buf_size) + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.SET_BUFFERSIZE_HOST, ctypes.byref(buf)) + if res < 0: + raise errors.JLinkException(res) + + @connection_required + def swo_set_emu_buffer_size(self, buf_size): + """Sets the size of the buffer used by the J-Link to collect SWO data. + + Args: + self (JLink): the ``JLink`` instance + buf_size (int): the new size of the emulator buffer + + Returns: + ``None`` + + Raises: + JLinkException: on error + """ + buf = ctypes.c_uint32(buf_size) + res = self._dll.JLINKARM_SWO_Control(enums.JLinkSWOCommands.SET_BUFFERSIZE_EMU, ctypes.byref(buf)) + if res < 0: + raise errors.JLinkException(res) + + @connection_required + def swo_supported_speeds(self, cpu_speed, num_speeds=3): + """Retrives a list of SWO speeds supported by both the target and the + connected J-Link. + + The supported speeds are returned in order from highest to lowest. + + Args: + self (JLink): the ``JLink`` instance + cpu_speed (int): the target's CPU speed in Hz + num_speeds (int): the number of compatible speeds to return + + Returns: + A list of compatible SWO speeds in Hz in order from highest to lowest. + """ + buf_size = num_speeds + buf = (ctypes.c_uint32 * buf_size)() + res = self._dll.JLINKARM_SWO_GetCompatibleSpeeds(cpu_speed, 0, buf, buf_size) + if res < 0: + raise errors.JLinkException(res) + return list(buf)[:res] + + @connection_required + def swo_read(self, offset, num_bytes, remove=False): + """Reads data from the SWO buffer. + + The data read is not automatically removed from the SWO buffer after + reading unless ``remove`` is ``True``. Otherwise the callee must + explicitly remove the data by calling ``.swo_flush()``. + + Args: + self (JLink): the ``JLink`` instance + offset (int): offset of first byte to be retrieved + num_bytes (int): number of bytes to read + remove (bool): if data should be removed from buffer after read + + Returns: + A list of bytes read from the SWO buffer. + """ + buf_size = ctypes.c_uint32(num_bytes) + buf = ctypes.c_uint8 * num_bytes(0) + self._dll.JLINKARM_SWO_Read(buf, offset, ctypes.byref(buf_size)) + buf_size = buf_size.value + if remove: + self.swo_flush(buf_size) + return list(buf)[:buf_size] + + @connection_required + def swo_read_stimulus(self, port, num_bytes): + """Reads the printable data via SWO. + + This method reads SWO for one stimulus port, which is all printable + data. + + Note: + Stimulus port ``0`` is used for ``printf`` debugging. + + Args: + self (JLink): the ``JLink`` instance + port (int): the stimulus port to read from, ``0 - 31`` + num_bytes (int): number of bytes to read + + Returns: + A list of bytes read via SWO. + + Raises: + ValueError: if ``port < 0`` or ``port > 31`` + """ + if port < 0 or port > 31: + raise ValueError('Invalid port number: %s' % port) + buf_size = num_bytes + buf = (ctypes.c_uint8 * buf_size)() + bytes_read = self._dll.JLINKARM_SWO_ReadStimulus(port, buf, buf_size) + return list(buf)[:bytes_read] + + @open_required + def rtt_start(self): + """Starts RTT processing, including background read of target data. + Args: + self (JLink): the ``JLink`` instance + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Control call fails. + """ + self.rtt_control(enums.JLinkRTTCommand.START, None) + + @open_required + def rtt_stop(self): + """Stops RTT on the J-Link and host side. + Args: + self (JLink): the ``JLink`` instance + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Control call fails. + """ + self.rtt_control(enums.JLinkRTTCommand.STOP, None) + + @open_required + def rtt_get_num_up_buffers(self): + """After starting RTT, get the current number of up buffers. + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of configured up buffers on the target. + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Control call fails. + """ + cmd = enums.JLinkRTTCommand.GETNUMBUF + dir = ctypes.c_int(enums.JLinkRTTDirection.UP) + return self.rtt_control(cmd, dir) + + @open_required + def rtt_get_num_down_buffers(self): + """After starting RTT, get the current number of down buffers. + Args: + self (JLink): the ``JLink`` instance + + Returns: + The number of configured down buffers on the target. + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Control call fails. + """ + cmd = enums.JLinkRTTCommand.GETNUMBUF + dir = ctypes.c_int(enums.JLinkRTTDirection.DOWN) + return self.rtt_control(cmd, dir) + + @open_required + def rtt_read(self, buffer_index, num_bytes): + """Reads data from the RTT buffer. + + This method will read at most num_bytes bytes from the specified + RTT buffer. The data is automatically removed from the RTT buffer. + If there are not num_bytes bytes waiting in the RTT buffer, the + entire contents of the RTT buffer will be read. + + Args: + self (JLink): the ``JLink`` instance + buffer_index (int): the index of the RTT buffer to read from + num_bytes (int): the maximum number of bytes to read + + Returns: + A list of bytes read from RTT. + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Read call fails. + """ + buf = (ctypes.c_ubyte * num_bytes)() + bytes_read = self._dll.JLINK_RTTERMINAL_Read(buffer_index, buf, num_bytes) + if bytes_read < 0: + raise errors.JLinkRTTException(bytes_read) + return list(buf)[:bytes_read] + + @open_required + def rtt_write(self, buffer_index, data): + """Writes data to the RTT buffer. + + This method will write at most len(data) bytes to the specified RTT + buffer. + + Args: + self (JLink): the ``JLink`` instance + buffer_index (int): the index of the RTT buffer to write to + data (list): the list of bytes to write to the RTT buffer + + Returns: + The number of bytes successfully written to the RTT buffer. + + Raises: + JLinkRTTException if the underlying JLINK_RTTERMINAL_Write call fails. + """ + buf_size = len(data) + buf = (ctypes.c_ubyte * buf_size)(*bytearray(data)) + bytes_written = self._dll.JLINK_RTTERMINAL_Write(buffer_index, buf, buf_size) + if bytes_written < 0: + raise errors.JLinkRTTException(bytes_written) + return bytes_written + + @open_required + def rtt_control(self, command, config): + """Issues an RTT Control command. + + All RTT control is done through a single API call which expects + specifically laid-out configuration structures. + + Args: + self (JLink): the ``JLink`` instance + command (int): the command to issue (see enums.JLinkRTTCommand) + config (ctypes type): the configuration to pass by reference. + + Returns: + An integer containing the result of the command. + """ + config_byref = ctypes.byref(config) if config is not None else None + res = self._dll.JLINK_RTTERMINAL_Control(command, config_byref) + if res < 0: + raise errors.JLinkRTTException(res) + return res + + @connection_required + def cp15_present(self): + """Returns whether target has CP15 co-processor. + + Returns: + ``True`` if the target has CP15 co-processor, otherwise ``False``. + """ + result = False + if self._dll.JLINKARM_CP15_IsPresent() != 0: + result = True + return result + + @open_required + def cp15_register_read(self, cr_n, op_1, cr_m, op_2): + """Reads value from specified coprocessor register. + + Args: + cr_n (int): CRn value + op_1 (int): Op1 value + cr_m (int): CRm value + op_2 (int): Op2 value + + Returns: + An integer containing the value of coprocessor register + + Raises: + JLinkException: on error + """ + value = ctypes.c_uint32(0) + p_value = ctypes.pointer(value) + res = self._dll.JLINKARM_CP15_ReadEx(cr_n, cr_m, op_1, op_2, p_value) + if res != 0: + raise errors.JLinkException(res) + else: + value = value.value + return value + + @open_required + def cp15_register_write(self, cr_n, op_1, cr_m, op_2, value): + """Writes value to specified coprocessor register. + + Args: + cr_n (int): CRn value + op_1 (int): Op1 value + cr_m (int): CRm value + op_2 (int): Op2 value + value (int): value to write + + Returns: + An integer containing the result of the command + + Raises: + JLinkException: on error + """ + res = self._dll.JLINKARM_CP15_WriteEx(cr_n, cr_m, op_1, op_2, value) + if res != 0: + raise errors.JLinkException(res) + return res +# okay decompiling ./pylink/jlink.pyc diff --git a/pylink/jlock.py b/pylink/jlock.py new file mode 100644 index 0000000..4a2ce22 --- /dev/null +++ b/pylink/jlock.py @@ -0,0 +1,114 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/jlock.py +import psutil, errno, tempfile, os + +class JLock(object): + __doc__ = 'Lockfile for accessing a particular J-Link.\n\n The J-Link SDK does not prevent accessing the same J-Link multiple times\n from the same process or multiple processes. As a result, a user can\n have the same J-Link being accessed by multiple processes. This class\n provides an interface to a lock-file like structure for the physical\n J-Links to ensure that any instance of a ``JLink`` with an open emulator\n connection will be the only one accessing that emulator.\n\n This class uses a PID-style lockfile to allow acquiring of the lockfile in\n the instances where the lockfile exists, but the process which created it\n is no longer running.\n\n To share the same emulator connection between multiple threads, processes,\n or functions, a single instance of a ``JLink`` should be created and passed\n between the threads and processes.\n\n Attributes:\n name: the name of the lockfile.\n path: full path to the lockfile.\n fd: file description of the lockfile.\n acquired: boolean indicating if the lockfile lock has been acquired.\n ' + SERIAL_NAME_FMT = '.pylink-usb-{}.lck' + IPADDR_NAME_FMT = '.pylink-ip-{}.lck' + + def __init__(self, serial_no): + """Creates an instance of a ``JLock`` and populates the name. + + Note: + This method may fail if there is no temporary directory in which to + have the lockfile placed. + + Args: + self (JLock): the ``JLock`` instance + serial_no (int): the serial number of the J-Link + + Returns: + ``None`` + """ + self.name = self.SERIAL_NAME_FMT.format(serial_no) + self.acquired = False + self.fd = None + self.path = None + self.path = os.path.join(tempfile.tempdir, self.name) + + def __del__(self): + """Cleans up the lockfile instance if it was acquired. + + Args: + self (JLock): the ``JLock`` instance + + Returns: + ``None`` + """ + self.release() + + def acquire(self): + """Attempts to acquire a lock for the J-Link lockfile. + + If the lockfile exists but does not correspond to an active process, + the lockfile is first removed, before an attempt is made to acquire it. + + Args: + self (Jlock): the ``JLock`` instance + + Returns: + ``True`` if the lock was acquired, otherwise ``False``. + + Raises: + OSError: on file errors. + """ + if os.path.exists(self.path): + try: + pid = None + with open(self.path, 'r') as f: + line = f.readline().strip() + pid = int(line) + if not psutil.pid_exists(pid): + os.remove(self.path) + except ValueError as e: + try: + os.remove(self.path) + finally: + e = None + del e + + except IOError as e: + try: + pass + finally: + e = None + del e + + try: + self.fd = os.open(self.path, os.O_CREAT | os.O_EXCL | os.O_RDWR) + to_write = '%s%s' % (os.getpid(), os.linesep) + os.write(self.fd, to_write.encode()) + except OSError as e: + try: + if not os.path.exists(self.path): + raise + return False + finally: + e = None + del e + + self.acquired = True + return True + + def release(self): + """Cleans up the lockfile if it was acquired. + + Args: + self (JLock): the ``JLock`` instance + + Returns: + ``False`` if the lock was not released or the lock is not acquired, + otherwise ``True``. + """ + if not self.acquired: + return False + os.close(self.fd) + if os.path.exists(self.path): + os.remove(self.path) + self.acquired = False + return True +# okay decompiling ./pylink/jlock.pyc diff --git a/pylink/library.py b/pylink/library.py new file mode 100644 index 0000000..2994707 --- /dev/null +++ b/pylink/library.py @@ -0,0 +1,346 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/library.py +from . import util +import ctypes +import ctypes.util as ctypes_util +import os, sys, tempfile + +class Library(object): + __doc__ = 'Wrapper to provide easy access to loading the J-Link SDK DLL.\n\n This class provides a convenience for finding and loading the J-Link DLL\n across multiple platforms, and accounting for the inconsistencies between\n Windows and nix-based platforms.\n\n Attributes:\n _standard_calls_: list of names of the methods for the API calls that\n must be converted to standard calling convention on the Windows\n platform.\n JLINK_SDK_NAME: name of the J-Link DLL on nix-based platforms.\n WINDOWS_JLINK_SDK_NAME: name of the J-Link DLL on Windows platforms.\n ' + _standard_calls_ = [ + 'JLINK_Configure', + 'JLINK_DownloadFile', + 'JLINK_GetAvailableLicense', + 'JLINK_GetPCode', + 'JLINK_PrintConfig', + 'JLINK_EraseChip', + 'JLINK_SPI_Transfer', + 'JLINK_GetpFunc', + 'JLINK_GetMemZones', + 'JLINK_ReadMemZonedEx', + 'JLINK_SetHookUnsecureDialog', + 'JLINK_WriteMemZonedEx', + 'JLINK_DIALOG_Configure', + 'JLINK_DIALOG_ConfigureEx', + 'JLINK_EMU_GPIO_GetProps', + 'JLINK_EMU_GPIO_GetState', + 'JLINK_EMU_GPIO_SetState', + 'JLINK_EMU_AddLicense', + 'JLINK_EMU_EraseLicenses', + 'JLINK_EMU_GetLicenses', + 'JLINK_HSS_GetCaps', + 'JLINK_HSS_Start', + 'JLINK_HSS_Stop', + 'JLINK_HSS_Read', + 'JLINK_POWERTRACE_Control', + 'JLINK_POWERTRACE_Read', + 'JLINK_RTTERMINAL_Control', + 'JLINK_RTTERMINAL_Read', + 'JLINK_RTTERMINAL_Write', + 'JLINK_STRACE_Config', + 'JLINK_STRACE_Control', + 'JLINK_STRACE_Read', + 'JLINK_STRACE_Start', + 'JLINK_STRACE_Stop', + 'JLINK_SWD_GetData', + 'JLINK_SWD_GetU8', + 'JLINK_SWD_GetU16', + 'JLINK_SWD_GetU32', + 'JLINK_SWD_StoreGetRaw', + 'JLINK_SWD_StoreRaw', + 'JLINK_SWD_SyncBits', + 'JLINK_SWD_SyncBytes', + 'JLINK_SetFlashProgProgressCallback'] + JLINK_SDK_NAME = 'libjlinkarm' + WINDOWS_32_JLINK_SDK_NAME = 'JLinkARM' + WINDOWS_64_JLINK_SDK_NAME = 'JLink_x64' + + @classmethod + def get_appropriate_windows_sdk_name(cls): + """Returns the appropriate JLink SDK library name on Windows depending + on 32bit or 64bit Python variant. + + SEGGER delivers two variants of their dynamic library on Windows: + - ``JLinkARM.dll`` for 32-bit platform + - ``JLink_x64.dll`` for 64-bit platform + + Args: + cls (Library): the ``Library`` class + + Returns: + The name of the library depending on the platform this module is run on. + + """ + if sys.maxsize == 9223372036854775807: + return Library.WINDOWS_64_JLINK_SDK_NAME + return Library.WINDOWS_32_JLINK_SDK_NAME + + @classmethod + def find_library_windows(cls): + r"""Loads the SEGGER DLL from the windows installation directory. + + On Windows, these are found either under: + - ``C:\Program Files\SEGGER\JLink`` + - ``C:\Program Files (x86)\SEGGER\JLink``. + + Args: + cls (Library): the ``Library`` class + + Returns: + The paths to the J-Link library files in the order that they are + found. + """ + dll = cls.get_appropriate_windows_sdk_name() + '.dll' + root = 'C:\\' + for d in os.listdir(root): + dir_path = os.path.join(root, d) + if d.startswith('Program Files'): + if os.path.isdir(dir_path): + dir_path = os.path.join(dir_path, 'SEGGER') + if not os.path.isdir(dir_path): + continue + else: + ds = filter(lambda x: x.startswith('JLink') +, os.listdir(dir_path)) + for jlink_dir in ds: + lib_path = os.path.join(dir_path, jlink_dir, dll) + if os.path.isfile(lib_path): + yield lib_path + + @classmethod + def find_library_linux(cls): + """Loads the SEGGER DLL from the root directory. + + On Linux, the SEGGER tools are installed under the ``/opt/SEGGER`` + directory with versioned directories having the suffix ``_VERSION``. + + Args: + cls (Library): the ``Library`` class + + Returns: + The paths to the J-Link library files in the order that they are + found. + """ + dll = Library.JLINK_SDK_NAME + root = os.path.join('/', 'opt', 'SEGGER') + for directory_name, subdirs, files in os.walk(root): + fnames = [] + x86_found = False + for f in files: + path = os.path.join(directory_name, f) + if os.path.isfile(path): + if f.startswith(dll): + fnames.append(f) + if '_x86' in path: + x86_found = True + + for fname in fnames: + fpath = os.path.join(directory_name, fname) + if util.is_os_64bit(): + if '_x86' not in fname: + yield fpath + else: + if x86_found: + if '_x86' in fname: + yield fpath + else: + yield fpath + + @classmethod + def find_library_darwin(cls): + r"""Loads the SEGGER DLL from the installed applications. + + This method accounts for the all the different ways in which the DLL + may be installed depending on the version of the DLL. Always uses + the first directory found. + + SEGGER's DLL is installed in one of three ways dependent on which + which version of the SEGGER tools are installed: + + ======== ============================================================ + Versions Directory + ======== ============================================================ + < 5.0.0 ``/Applications/SEGGER/JLink\ NUMBER`` + < 6.0.0 ``/Applications/SEGGER/JLink/libjlinkarm.major.minor.dylib`` + >= 6.0.0 ``/Applications/SEGGER/JLink/libjlinkarm`` + ======== ============================================================ + + Args: + cls (Library): the ``Library`` class + + Returns: + The path to the J-Link library files in the order they are found. + """ + dll = Library.JLINK_SDK_NAME + root = os.path.join('/', 'Applications', 'SEGGER') + if not os.path.isdir(root): + return + for d in os.listdir(root): + dir_path = os.path.join(root, d) + if os.path.isdir(dir_path): + if d.startswith('JLink'): + files = list((f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f)))) + if dll + '.dylib' in files: + yield os.path.join(dir_path, dll + '.dylib') + else: + for f in files: + if f.startswith(dll): + yield os.path.join(dir_path, f) + + def __init__(self, dllpath=None): + """Initializes an instance of a ``Library``. + + Loads the default J-Link DLL if ``dllpath`` is ``None``, otherwise + loads the DLL specified by the given ``dllpath``. + + Args: + self (Library): the ``Library`` instance + dllpath (str): the DLL to load into the library + + Returns: + ``None`` + """ + self._lib = None + self._winlib = None + self._path = None + self._windows = sys.platform.startswith('win') + self._cygwin = sys.platform.startswith('cygwin') + self._temp = None + if self._windows or self._cygwin: + self._sdk = self.get_appropriate_windows_sdk_name() + else: + self._sdk = self.JLINK_SDK_NAME + if dllpath is not None: + self.load(dllpath) + else: + self.load_default() + + def __del__(self): + """Cleans up the temporary DLL file created when the lib was loaded. + + Args: + self (Library): the ``Library`` instance + + Returns: + ``None`` + """ + self.unload() + + def load_default(self): + """Loads the default J-Link SDK DLL. + + The default J-Link SDK is determined by first checking if ``ctypes`` + can find the DLL, then by searching the platform-specific paths. + + Args: + self (Library): the ``Library`` instance + + Returns: + ``True`` if the DLL was loaded, otherwise ``False``. + """ + path = ctypes_util.find_library(self._sdk) + if not path is None or self._windows or self._cygwin: + path = next(self.find_library_windows(), None) + else: + if sys.platform.startswith('linux'): + path = next(self.find_library_linux(), None) + else: + if sys.platform.startswith('darwin'): + path = next(self.find_library_darwin(), None) + if path is not None: + return self.load(path) + return False + + def load(self, path=None): + """Loads the specified DLL, if any, otherwise re-loads the current DLL. + + If ``path`` is specified, loads the DLL at the given ``path``, + otherwise re-loads the DLL currently specified by this library. + + Note: + This creates a temporary DLL file to use for the instance. This is + necessary to work around a limitation of the J-Link DLL in which + multiple J-Links cannot be accessed from the same process. + + Args: + self (Library): the ``Library`` instance + path (path): path to the DLL to load + + Returns: + ``True`` if library was loaded successfully. + + Raises: + OSError: if there is no J-LINK SDK DLL present at the path. + + See Also: + `J-Link Multi-session `_. + """ + self.unload() + self._path = path or self._path + if self._windows or self._cygwin: + suffix = '.dll' + else: + if sys.platform.startswith('darwin'): + suffix = '.dylib' + else: + suffix = '.so' + tf = tempfile.NamedTemporaryFile(delete=False, suffix=suffix) + with open(tf.name, 'wb') as outputfile: + with open(self._path, 'rb') as inputfile: + outputfile.write(inputfile.read()) + tf.close() + self._temp = tf + self._lib = ctypes.cdll.LoadLibrary(tf.name) + if self._windows: + self._winlib = ctypes.windll.LoadLibrary(tf.name) + for stdcall in self._standard_calls_: + if hasattr(self._winlib, stdcall): + setattr(self._lib, stdcall, getattr(self._winlib, stdcall)) + + return True + + def unload(self): + """Unloads the library's DLL if it has been loaded. + + This additionally cleans up the temporary DLL file that was created + when the library was loaded. + + Args: + self (Library): the ``Library`` instance + + Returns: + ``True`` if the DLL was unloaded, otherwise ``False``. + """ + unloaded = False + if self._lib is not None: + if self._winlib is not None: + ctypes.windll.kernel32.FreeLibrary.argtypes = ( + ctypes.c_void_p,) + ctypes.windll.kernel32.FreeLibrary(self._lib._handle) + ctypes.windll.kernel32.FreeLibrary(self._winlib._handle) + self._lib = None + self._winlib = None + unloaded = True + else: + del self._lib + self._lib = None + unloaded = True + if self._temp is not None: + os.remove(self._temp.name) + self._temp = None + return unloaded + + def dll(self): + """Returns the DLL for the underlying shared library. + + Args: + self (Library): the ``Library`` instance + + Returns: + A ``ctypes`` DLL instance if one was loaded, otherwise ``None``. + """ + return self._lib +# okay decompiling ./pylink/library.pyc diff --git a/pylink/registers.py b/pylink/registers.py new file mode 100644 index 0000000..02b6f6f --- /dev/null +++ b/pylink/registers.py @@ -0,0 +1,206 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/registers.py +import ctypes + +class IDCodeRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'This class holds the different bit masks for the IDCode register.\n\n Attributes:\n valid: validity bit, should always be ``0``.\n manufactuer: the JEDEC Manufacturer ID.\n part_no: the part number defined by the manufacturer.\n version_code: the version code.\n ' + _fields_ = [ + ( + 'valid', ctypes.c_uint32, 1), + ( + 'manufacturer', ctypes.c_uint32, 11), + ( + 'part_no', ctypes.c_uint32, 16), + ( + 'version_code', ctypes.c_uint32, 4)] + + +class IDCodeRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the IDCode register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', IDCodeRegisterBits), + ( + 'value', ctypes.c_uint32)] + + +class AbortRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'This class holds the different bit mask for the Abort Register.\n\n Attributes:\n DAPABORT: write ``1`` to trigger a DAP abort.\n STKCMPCLR: write ``1`` to clear the ``STICKYCMP`` sticky compare flag\n (only supported on SW-DP).\n STKERRCLR: write ``1`` to clear the ``STICKYERR`` sticky error flag\n (only supported on SW-DP).\n WDERRCLR: write ``1`` to clear the ``WDATAERR`` write data error flag\n (only supported on SW-DP).\n ORUNERRCLR: write ``1`` to clear the ``STICKYORUN`` overrun error flag\n (only supported on SW-DP).\n ' + _fields_ = [ + ( + 'DAPABORT', ctypes.c_uint32, 1), + ( + 'STKCMPCLR', ctypes.c_uint32, 1), + ( + 'STKERRCLR', ctypes.c_uint32, 1), + ( + 'WDERRCLR', ctypes.c_uint32, 1), + ( + 'ORUNERRCLR', ctypes.c_uint32, 1), + ( + 'RESERVED', ctypes.c_uint32, 27)] + + +class AbortRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the abort register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', AbortRegisterBits), + ( + 'value', ctypes.c_uint32)] + + +class ControlStatusRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'This class holds the different bit masks for the DP Control / Status\n Register bit assignments.\n\n Attributes:\n ORUNDETECT: if set, enables overrun detection.\n STICKYORUN: if overrun is enabled, is set when overrun occurs.\n TRNMODE: transfer mode for acess port operations.\n STICKYCMP: is set when a match occurs on a pushed compare or verify\n operation.\n STICKYERR: is set when an error is returned by an access port\n transaction.\n READOK: is set when the response to a previous access port or ``RDBUFF``\n was ``OK``.\n WDATAERR: set to ``1`` if a Write Data Error occurs.\n MASKLANE: bytes to be masked in pushed compare and verify operations.\n TRNCNT: transaction counter.\n RESERVED: reserved.\n CDBGRSTREQ: debug reset request.\n CDBGRSTACK: debug reset acknowledge.\n CDBGPWRUPREQ: debug power-up request.\n CDBGPWRUPACK: debug power-up acknowledge.\n CSYSPWRUPREQ: system power-up request\n CSYSPWRUPACK: system power-up acknowledge.\n\n See also:\n See the ARM documentation on the significance of these masks\n `here `_.\n ' + _fields_ = [ + ( + 'ORUNDETECT', ctypes.c_uint32, 1), + ( + 'STICKYORUN', ctypes.c_uint32, 1), + ( + 'TRNMODE', ctypes.c_uint32, 2), + ( + 'STICKYCMP', ctypes.c_uint32, 1), + ( + 'STICKYERR', ctypes.c_uint32, 1), + ( + 'READOK', ctypes.c_uint32, 1), + ( + 'WDATAERR', ctypes.c_uint32, 1), + ( + 'MASKLANE', ctypes.c_uint32, 4), + ( + 'TRNCNT', ctypes.c_uint32, 12), + ( + 'RESERVED', ctypes.c_uint32, 2), + ( + 'CDBGRSTREQ', ctypes.c_uint32, 1), + ( + 'CDBGRSTACK', ctypes.c_uint32, 1), + ( + 'CDBGPWRUPREQ', ctypes.c_uint32, 1), + ( + 'CDBGPWRUPACK', ctypes.c_uint32, 1), + ( + 'CSYSPWRUPREQ', ctypes.c_uint32, 1), + ( + 'CSYSPWRUPACK', ctypes.c_uint32, 1)] + + +class ControlStatusRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the control/status register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', ControlStatusRegisterBits), + ( + 'value', ctypes.c_uint32)] + + +class SelectRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'This class holds the different bit masks for the AP Select Register.\n\n Attributes:\n CTRLSEL: SW-DP debug port address bank select.\n RESERVED_A: reserved.\n APBANKSEL: selects the active four-word register window on the current\n access port.\n RESERVED_B: reserved.\n APSEL: selects the current access port.\n ' + _fields_ = [ + ( + 'CTRLSEL', ctypes.c_uint32, 1), + ( + 'RESERVED_A', ctypes.c_uint32, 3), + ( + 'APBANKSEL', ctypes.c_uint32, 4), + ( + 'RESERVED_B', ctypes.c_uint32, 16), + ( + 'APSEL', ctypes.c_uint32, 8)] + + +class SelectRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the select register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', SelectRegisterBits), + ( + 'value', ctypes.c_uint32)] + + +class MDMAPControlRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'This class holds the different bit masks for the MDM-AP Control\n Register.\n\n Attributes:\n flash_mass_erase: set to cause a mass erase, this is cleared\n automatically when a mass erase finishes.\n debug_disable: set to disable debug, clear to allow debug.\n debug_request: set to force the core to halt.\n sys_reset_request: set to force a system reset.\n core_hold_reset: set to suspend the core in reset at the end of reset\n sequencing.\n VLLDBGREQ: set to hold the system in reset after the next recovery from\n VLLSx (Very Low Leakage Stop).\n VLLDBGACK: set to release a system held in reset following a VLLSx\n (Very Low Leakage Stop) recovery.\n VLLSTATACK: set to acknowledge that the DAP LLS (Low Leakage Stop) and\n VLLS (Very Low Leakage Stop) status bits have read.\n ' + _fields_ = [ + ( + 'flash_mass_erase', ctypes.c_uint8, 1), + ( + 'debug_disable', ctypes.c_uint8, 1), + ( + 'debug_request', ctypes.c_uint8, 1), + ( + 'sys_reset_request', ctypes.c_uint8, 1), + ( + 'core_hold_reset', ctypes.c_uint8, 1), + ( + 'VLLDBGREQ', ctypes.c_uint8, 1), + ( + 'VLLDBGACK', ctypes.c_uint8, 1), + ( + 'VLLSTATACK', ctypes.c_uint8, 1)] + + +class MDMAPControlRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the MDM-AP control register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', MDMAPControlRegisterBits), + ( + 'value', ctypes.c_uint8)] + + +class MDMAPStatusRegisterBits(ctypes.LittleEndianStructure): + __doc__ = 'Holds the bit masks for the MDM-AP Status Register.\n\n Attributes:\n flash_mass_erase_ack: cleared after a system reset, indicates that a\n flash mass erase was acknowledged.\n flash_ready: indicates that flash has been initialized and can be\n configured.\n system_security: if set, system is secure and debugger cannot access the\n memory or system bus.\n system_reset: ``1`` if system is in reset, otherwise ``0``.\n mass_erase_enabled: ``1`` if MCU can be mass erased, otherwise ``0``.\n low_power_enabled: ``1`` if low power stop mode is enabled, otherwise ``0``.\n very_low_power_mode: ``1`` if device is in very low power mode.\n LLSMODEEXIT: indicates an exit from LLS mode has occurred.\n VLLSxMODEEXIT: indicates an exit from VLLSx mode has occured.\n core_halted; indicates core has entered debug halt mode.\n core_deep_sleep: indicates core has entered a low power mode.\n core_sleeping: indicates the core has entered a low power mode.\n\n Note:\n if ``core_sleeping & !core_deep_sleep``, then the core is in VLPW (very\n low power wait) mode, otherwise if ``core_sleeping & core_deep_sleep``,\n then it is in VLPS (very low power stop) mode.\n ' + _fields_ = [ + ( + 'flash_mass_erase_ack', ctypes.c_uint32, 1), + ( + 'flash_ready', ctypes.c_uint32, 1), + ( + 'system_security', ctypes.c_uint32, 1), + ( + 'system_reset', ctypes.c_uint32, 1), + ( + 'RESERVED_A', ctypes.c_uint32, 1), + ( + 'mass_erase_enabled', ctypes.c_uint32, 1), + ( + 'backdoor_access_enabled', ctypes.c_uint32, 1), + ( + 'low_power_enabled', ctypes.c_uint32, 1), + ( + 'very_low_power_mode', ctypes.c_uint32, 1), + ( + 'LLSMODEEXIT', ctypes.c_uint32, 1), + ( + 'VLLSxMODEEXIT', ctypes.c_uint32, 1), + ( + 'RESERVED_B', ctypes.c_uint32, 5), + ( + 'core_halted', ctypes.c_uint32, 1), + ( + 'core_deep_sleep', ctypes.c_uint32, 1), + ( + 'core_sleeping', ctypes.c_uint32, 1), + ( + 'RESERVED_C', ctypes.c_uint32, 13)] + + +class MDMAPStatusRegisterFlags(ctypes.Union): + __doc__ = 'Mask for the MDM-AP status register bits.\n\n Attributes:\n value: the value stored in the mask.\n ' + _anonymous_ = ('bit', ) + _fields_ = [ + ( + 'bit', MDMAPStatusRegisterBits), + ( + 'value', ctypes.c_uint32)] +# okay decompiling ./pylink/registers.pyc diff --git a/pylink/structs.py b/pylink/structs.py new file mode 100644 index 0000000..6dd8835 --- /dev/null +++ b/pylink/structs.py @@ -0,0 +1,907 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/structs.py +from . import enums +import ctypes + +class JLinkConnectInfo(ctypes.Structure): + __doc__ = 'J-Link connection info structure.\n\n Attributes:\n SerialNumber: J-Link serial number.\n Connection: type of connection (e.g. ``enums.JLinkHost.USB``)\n USBAddr: USB address if connected via USB.\n aIPAddr: IP address if connected via IP.\n Time: Time period (ms) after which UDP discover answer was received.\n Time_us: Time period (uS) after which UDP discover answer was received.\n HWVersion: Hardware version of J-Link, if connected via IP.\n abMACAddr: MAC Address, if connected via IP.\n acProduct: Product name, if connected via IP.\n acNickname: Nickname, if connected via IP.\n acFWString: Firmware string, if connected via IP.\n IsDHCPAssignedIP: Is IP address reception via DHCP.\n IsDHCPAssignedIPIsValid: True if connected via IP.\n NumIPConnections: Number of IP connections currently established.\n NumIPConnectionsIsValid: True if connected via IP.\n aPadding: Bytes reserved for future use.\n ' + _fields_ = [ + ( + 'SerialNumber', ctypes.c_uint32), + ( + 'Connection', ctypes.c_ubyte), + ( + 'USBAddr', ctypes.c_uint32), + ( + 'aIPAddr', ctypes.c_uint8 * 16), + ( + 'Time', ctypes.c_int), + ( + 'Time_us', ctypes.c_uint64), + ( + 'HWVersion', ctypes.c_uint32), + ( + 'abMACAddr', ctypes.c_uint8 * 6), + ( + 'acProduct', ctypes.c_char * 32), + ( + 'acNickname', ctypes.c_char * 32), + ( + 'acFWString', ctypes.c_char * 112), + ( + 'IsDHCPAssignedIP', ctypes.c_char), + ( + 'IsDHCPAssignedIPIsValid', ctypes.c_char), + ( + 'NumIPConnections', ctypes.c_char), + ( + 'NumIPConnectionsIsValid', ctypes.c_char), + ( + 'aPadding', ctypes.c_uint8 * 34)] + + def __repr__(self): + """Returns a representation of this class. + + Args: + self (JLinkConnectInfo): the ``JlinkConnectInfo`` instance + + Returns: + String representation of the class. + """ + return 'JLinkConnectInfo(%s)' % self.__str__() + + def __str__(self): + """Returns a string representation of the connection info. + + Args: + self (JLinkConnectInfo): the ``JLinkConnectInfo`` instance + + Returns: + String specifying the product, its serial number, and the type of + connection that it has (one of USB or IP). + """ + conn = 'USB' if self.Connection == 1 else 'IP' + return '%s ' % (self.acProduct.decode(), self.SerialNumber, conn) + + +class JLinkFlashArea(ctypes.Structure): + __doc__ = 'Definition for a region of Flash.\n\n Attributes:\n Addr: address where the flash area starts.\n Size: size of the flash area.\n ' + _fields_ = [ + ( + 'Addr', ctypes.c_uint32), + ( + 'Size', ctypes.c_uint32)] + + def __repr__(self): + """Returns a representation of the instance. + + Args: + self (FlashArea): the ``FlashArea`` instance + + Returns: + String representation of the Flash Area. + """ + return '%s(%s)' % (self.__class__.__name__, self.__str__()) + + def __str__(self): + """Returns a string representation of the instance. + + Args: + self (FlashArea): the ``FlashArea`` instance + + Returns: + String specifying address of flash region, and its size. + """ + return 'Address = 0x%x, Size = %s' % (self.Addr, self.Size) + + +class JLinkRAMArea(JLinkFlashArea): + __doc__ = 'Definition for a region of RAM.\n\n Attributes:\n Addr: address where the flash area starts.\n Size: size of the flash area.\n ' + + +class JLinkDeviceInfo(ctypes.Structure): + __doc__ = 'J-Link device information.\n\n This structure is used to represent a device that is supported by the\n J-Link.\n\n Attributes:\n SizeOfStruct: Size of the struct (DO NOT CHANGE).\n sName: name of the device.\n CoreId: core identifier of the device.\n FlashAddr: base address of the internal flash of the device.\n RAMAddr: base address of the internal RAM of the device.\n EndianMode: the endian mode of the device (0 -> only little endian,\n 1 -> only big endian, 2 -> both).\n FlashSize: total flash size in bytes.\n RAMSize: total RAM size in bytes.\n sManu: device manufacturer.\n aFlashArea: a list of ``JLinkFlashArea`` instances.\n aRamArea: a list of ``JLinkRAMArea`` instances.\n Core: CPU core.\n ' + _fields_ = [ + ( + 'SizeofStruct', ctypes.c_uint32), + ( + 'sName', ctypes.POINTER(ctypes.c_char)), + ( + 'CoreId', ctypes.c_uint32), + ( + 'FlashAddr', ctypes.c_uint32), + ( + 'RAMAddr', ctypes.c_uint32), + ( + 'EndianMode', ctypes.c_char), + ( + 'FlashSize', ctypes.c_uint32), + ( + 'RAMSize', ctypes.c_uint32), + ( + 'sManu', ctypes.POINTER(ctypes.c_char)), + ( + 'aFlashArea', JLinkFlashArea * 32), + ( + 'aRAMArea', JLinkRAMArea * 32), + ( + 'Core', ctypes.c_uint32)] + + def __init__(self, *args, **kwargs): + (super(JLinkDeviceInfo, self).__init__)(*args, **kwargs) + self.SizeofStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a representation of this instance. + + Args: + self (JLinkDeviceInfo): the ``JLinkDeviceInfo`` instance + + Returns: + Returns a string representation of the instance. + """ + return 'JLinkDeviceInfo(%s)' % self.__str__() + + def __str__(self): + """Returns a string representation of this instance. + + Args: + self (JLinkDeviceInfo): the ``JLinkDeviceInfo`` instance + + Returns: + Returns a string specifying the device name, core, and manufacturer. + """ + manu = self.manufacturer + return '%s ' % (self.name, self.Core, manu) + + @property + def name(self): + """Returns the name of the device. + + Args: + self (JLinkDeviceInfo): the ``JLinkDeviceInfo`` instance + + Returns: + Device name. + """ + return ctypes.cast(self.sName, ctypes.c_char_p).value.decode() + + @property + def manufacturer(self): + """Returns the name of the manufacturer of the device. + + Args: + self (JLinkDeviceInfo): the ``JLinkDeviceInfo`` instance + + Returns: + Manufacturer name. + """ + buf = ctypes.cast(self.sManu, ctypes.c_char_p).value + if buf: + return buf.decode() + + +class JLinkHardwareStatus(ctypes.Structure): + __doc__ = 'Definition for the hardware status information for a J-Link.\n\n Attributes:\n VTarget: target supply voltage.\n tck: measured state of TCK pin.\n tdi: measured state of TDI pin.\n tdo: measured state of TDO pin.\n tms: measured state of TMS pin.\n tres: measured state of TRES pin.\n trst: measured state of TRST pin.\n ' + _fields_ = [ + ( + 'VTarget', ctypes.c_uint16), + ( + 'tck', ctypes.c_uint8), + ( + 'tdi', ctypes.c_uint8), + ( + 'tdo', ctypes.c_uint8), + ( + 'tms', ctypes.c_uint8), + ( + 'tres', ctypes.c_uint8), + ( + 'trst', ctypes.c_uint8)] + + def __repr__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkHardwareStatus): the ``JlinkHardwareStatus`` instance + + Returns: + String representation of the instance. + """ + return '%s(VTarget=%dmV)' % (self.__class__.__name__, self.voltage) + + @property + def voltage(self): + """Returns the target supply voltage. + + This is an alias for ``.VTarget``. + + Args: + self (JLInkHardwareStatus): the ``JLinkHardwareStatus`` instance + + Returns: + Target supply voltage as an integer. + """ + return self.VTarget + + +class JLinkGPIODescriptor(ctypes.Structure): + __doc__ = 'Definition for the structure that details the name and capabilities of a\n user-controllable GPIO.\n\n Attributes:\n acName: name of the GPIO.\n Caps: bitfield of capabilities.\n ' + _fields_ = [ + ( + 'acName', ctypes.c_char * 32), + ( + 'Caps', ctypes.c_uint32)] + + def __repr__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkGPIODescriptor): the ``JLinkGPIODescriptor`` instance + + Returns: + String representation of the instance. + """ + return '%s(%s)' % (self.__class__.__name__, self.__str__()) + + def __str__(self): + """Returns the GPIO name. + + Args: + self (JLinkGPIODescriptor): the ``JLInkGPIODescriptor`` instance + + Returns: + GPIO name. + """ + return self.acName.decode() + + +class JLinkMemoryZone(ctypes.Structure): + __doc__ = 'Represents a CPU memory zone.\n\n Attributes:\n sName: initials of the memory zone.\n sDesc: name of the memory zone.\n VirtAddr: start address of the virtual address space of the memory zone.\n abDummy: reserved for future use.\n ' + _fields_ = [ + ( + 'sName', ctypes.c_char_p), + ( + 'sDesc', ctypes.c_char_p), + ( + 'VirtAddr', ctypes.c_uint64), + ( + 'abDummy', ctypes.c_uint8 * 16)] + + def __repr__(self): + """Returns a string representation of the instance + + Args: + self: the ``JLinkMemoryZone`` instance + + Returns: + String representation of the instance. + """ + return '%s(%s)' % (self.__class__.__name__, self.__str__()) + + def __str__(self): + """Returns a formatted string describing the memory zone. + + Args: + self: the ``JLinkMemoryZone`` instance + + Returns: + String representation of the memory zone. + """ + return '%s ' % (self.sName, self.sDesc, self.VirtAddr) + + @property + def name(self): + """Alias for the memory zone name. + + Args: + self (JLinkMemoryZone): the ``JLinkMemoryZone`` instance + + Returns: + The memory zone name. + """ + return self.sName + + +class JLinkSpeedInfo(ctypes.Structure): + __doc__ = "Represents information about an emulator's supported speeds.\n\n The emulator can support all target interface speeds calculated by dividing\n the base frequency by atleast ``MinDiv``.\n\n Attributes:\n SizeOfStruct: the size of this structure.\n BaseFreq: Base frequency (in HZ) used to calculate supported speeds.\n MinDiv: minimum divider allowed to divide the base frequency.\n SupportAdaptive: ``1`` if emulator supports adaptive clocking, otherwise\n ``0``.\n " + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_uint32), + ( + 'BaseFreq', ctypes.c_uint32), + ( + 'MinDiv', ctypes.c_uint16), + ( + 'SupportAdaptive', ctypes.c_uint16)] + + def __init__(self): + super(JLinkSpeedInfo, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkSpeedInfo): the ``JLinkSpeedInfo`` instance + + Returns: + String representation of the instance. + """ + return self.__str__() + + def __str__(self): + """Returns this instance formatted as a string. + + Args: + self (JLinkSpeedInfo): the ``JLinkSpeedInfo`` instance + + Returns: + String formatted instance. + """ + return '%s(Freq=%sHz)' % (self.__class__.__name__, self.BaseFreq) + + +class JLinkSWOStartInfo(ctypes.Structure): + __doc__ = 'Represents configuration information for collecting Serial Wire Output\n (SWO) information.\n\n Attributes:\n SizeofStruct: size of the structure.\n Interface: the interface type used for SWO.\n Speed: the frequency used for SWO communication in Hz.\n\n Note:\n You should *never* change ``.SizeofStruct`` or ``.Interface``.\n ' + _fields_ = [ + ( + 'SizeofStruct', ctypes.c_uint32), + ( + 'Interface', ctypes.c_uint32), + ( + 'Speed', ctypes.c_uint32)] + + def __init__(self): + super(JLinkSWOStartInfo, self).__init__() + self.SizeofStruct = ctypes.sizeof(self) + self.Interface = enums.JLinkSWOInterfaces.UART + + def __repr__(self): + """Returns a representation of this instance. + + Args: + self (JLinkSWOStartInfo): the ``JLinkSWOStartInfo`` instance + + Returns: + The string representation of this instance. + """ + return self.__str__() + + def __str__(self): + """Returns a string representation of this instance. + + Args: + self (JLinkSWOStartInfo): the ``JLinkSWOStartInfo`` instance + + Returns: + The string representation of this instance. + """ + return '%s(Speed=%sHz)' % (self.__class__.__name__, self.Speed) + + +class JLinkSWOSpeedInfo(ctypes.Structure): + __doc__ = "Structure representing information about target's supported SWO speeds.\n\n To calculate the supported SWO speeds, the base frequency is taken and\n divide by a number in the range of ``[ MinDiv, MaxDiv ]``.\n\n Attributes:\n SizeofStruct: size of the structure.\n Interface: interface type for the speed information.\n BaseFreq: base frequency (Hz) used to calculate supported SWO speeds.\n MinDiv: minimum divider allowed to divide the base frequency.\n MaxDiv: maximum divider allowed to divide the base frequency.\n MinPrescale: minimum prescaler allowed to adjust the base frequency.\n MaxPrescale: maximum prescaler allowed to adjust the base frequency.\n\n Note:\n You should *never* change ``.SizeofStruct`` or ``.Interface``.\n " + _fields_ = [ + ( + 'SizeofStruct', ctypes.c_uint32), + ( + 'Interface', ctypes.c_uint32), + ( + 'BaseFreq', ctypes.c_uint32), + ( + 'MinDiv', ctypes.c_uint32), + ( + 'MaxDiv', ctypes.c_uint32), + ( + 'MinPrescale', ctypes.c_uint32), + ( + 'MaxPrescale', ctypes.c_uint32)] + + def __init__(self): + super(JLinkSWOSpeedInfo, self).__init__() + self.SizeofStruct = ctypes.sizeof(self) + self.Interface = enums.JLinkSWOInterfaces.UART + + def __repr__(self): + """Returns a representation of the instance. + + Args: + self (JLinkSWOSpeedInfo): the ``JLinkSWOSpeedInfo`` instance + + Returns: + ``None`` + """ + return self.__str__() + + def __str__(self): + """Returns a string representaton of the instance. + + Args: + self (JLinkSWOSpeedInfo): the ``JLinkSWOSpeedInfo`` instance + + Returns: + ``None`` + """ + return '%s(Interface=UART, Freq=%sHz)' % (self.__class__.__name__, self.BaseFreq) + + +class JLinkMOEInfo(ctypes.Structure): + __doc__ = 'Structure representing the Method of Debug Entry (MOE).\n\n The method of debug entry is a reason for which a CPU has stopped. At any\n given time, there may be multiple methods of debug entry.\n\n Attributes:\n HaltReason: reason why the CPU stopped.\n Index: if cause of CPU stop was a code/data breakpoint, this identifies\n the index of the code/data breakpoint unit which causes the CPU to\n stop, otherwise it is ``-1``.\n ' + _fields_ = [ + ( + 'HaltReason', ctypes.c_uint32), + ( + 'Index', ctypes.c_int)] + + def __repr__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + A string representation of the instance. + """ + return '%s(%s)' % (self.__class__.__name__, self.__str__()) + + def __str__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + A string representation of the instance. + """ + d = enums.JLinkHaltReasons.__dict__ + s = next((k for k, v in d.items() if v == self.HaltReason)) + if self.dbgrq(): + return s + return s.replace('_', ' ').title() + + def dbgrq(self): + """Returns whether this a DBGRQ. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + ``True`` if this is a DBGRQ, otherwise ``False``. + """ + return self.HaltReason == enums.JLinkHaltReasons.DBGRQ + + def code_breakpoint(self): + """Returns whether this a code breakpoint. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + ``True`` if this is a code breakpoint, otherwise ``False``. + """ + return self.HaltReason == enums.JLinkHaltReasons.CODE_BREAKPOINT + + def data_breakpoint(self): + """Returns whether this a data breakpoint. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + ``True`` if this is a data breakpoint, otherwise ``False``. + """ + return self.HaltReason == enums.JLinkHaltReasons.DATA_BREAKPOINT + + def vector_catch(self): + """Returns whether this a vector catch. + + Args: + self (JLinkMOEInfo): the ``JLinkMOEInfo`` instance + + Returns: + ``True`` if this is a vector catch, otherwise ``False``. + """ + return self.HaltReason == enums.JLinkHaltReasons.VECTOR_CATCH + + +class JLinkBreakpointInfo(ctypes.Structure): + __doc__ = 'Class representing information about a breakpoint.\n\n Attributes:\n SizeOfStruct: the size of the structure (this should not be modified).\n Handle: breakpoint handle.\n Addr: address of where the breakpoint has been set.\n Type: type flags which were specified when the breakpoint was created.\n ImpFlags: describes the current state of the breakpoint.\n UseCnt: describes how often the breakpoint is set at the same address.\n ' + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_uint32), + ( + 'Handle', ctypes.c_uint32), + ( + 'Addr', ctypes.c_uint32), + ( + 'Type', ctypes.c_uint32), + ( + 'ImpFlags', ctypes.c_uint32), + ( + 'UseCnt', ctypes.c_uint32)] + + def __init__(self): + super(JLinkBreakpointInfo, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a formatted string describing the breakpoint. + + Args: + self (JLinkBreakpointInfo): the ``JLinkBreakpointInfo`` instance + + Returns: + Stirng representation of the breakpoint. + """ + return self.__str__() + + def __str__(self): + """Returns a formatted string describing the breakpoint. + + Args: + self (JLinkBreakpointInfo): the ``JLinkBreakpointInfo`` instance + + Returns: + Stirng representation of the breakpoint. + """ + name = self.__class__.__name__ + return '%s(Handle %d, Address %d)' % (name, self.Handle, self.Addr) + + def software_breakpoint(self): + """Returns whether this is a software breakpoint. + + Args: + self (JLinkBreakpointInfo): the ``JLinkBreakpointInfo`` instance + + Returns: + ``True`` if the breakpoint is a software breakpoint, otherwise + ``False``. + """ + software_types = [ + enums.JLinkBreakpoint.SW_RAM, + enums.JLinkBreakpoint.SW_FLASH, + enums.JLinkBreakpoint.SW] + return any((self.Type & stype for stype in software_types)) + + def hardware_breakpoint(self): + """Returns whether this is a hardware breakpoint. + + Args: + self (JLinkBreakpointInfo): the ``JLinkBreakpointInfo`` instance + + Returns: + ``True`` if the breakpoint is a hardware breakpoint, otherwise + ``False``. + """ + return self.Type & enums.JLinkBreakpoint.HW + + def pending(self): + """Returns if this breakpoint is pending. + + Args: + self (JLinkBreakpointInfo): the ``JLinkBreakpointInfo`` instance + + Returns: + ``True`` if the breakpoint is still pending, otherwise ``False``. + """ + return self.ImpFlags & enums.JLinkBreakpointImplementation.PENDING + + +class JLinkDataEvent(ctypes.Structure): + __doc__ = 'Class representing a data event.\n\n A data may halt the CPU, trigger SWO output, or trigger trace output.\n\n Attributes:\n SizeOfStruct: the size of the structure (this should not be modified).\n Type: the type of the data event (this should not be modified).\n Addr: the address on which the watchpoint was set\n AddrMask: the address mask used for comparision.\n Data: the data on which the watchpoint has been set.\n DataMask: the data mask used for comparision.\n Access: the control data on which the event has been set.\n AccessMask: the control mask used for comparison.\n ' + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_int), + ( + 'Type', ctypes.c_int), + ( + 'Addr', ctypes.c_uint32), + ( + 'AddrMask', ctypes.c_uint32), + ( + 'Data', ctypes.c_uint32), + ( + 'DataMask', ctypes.c_uint32), + ( + 'Access', ctypes.c_uint8), + ( + 'AccessMask', ctypes.c_uint8)] + + def __init__(self): + super(JLinkDataEvent, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + self.Type = enums.JLinkEventTypes.BREAKPOINT + + def __repr__(self): + """Returns a string representation of the data event. + + Args: + self (JLinkDataEvent): the ``JLinkDataEvent`` instance + + Returns: + A string representation of the data event. + """ + return self.__str__() + + def __str__(self): + """Returns a string representation of the data event. + + Args: + self (JLinkDataEvent): the ``JLinkDataEvent`` instance + + Returns: + A string representation of the data event. + """ + name = self.__class__.__name__ + return '%s(Type %d, Address %d)' % (name, self.Type, self.Addr) + + +class JLinkWatchpointInfo(ctypes.Structure): + __doc__ = 'Class representing information about a watchpoint.\n\n Attributes:\n SizeOfStruct: the size of the structure (this should not be modified).\n Handle: the watchpoint handle.\n Addr: the address the watchpoint was set at.\n AddrMask: the address mask used for comparison.\n Data: the data on which the watchpoint was set.\n DataMask: the data mask used for comparision.\n Ctrl: the control data on which the breakpoint was set.\n CtrlMask: the control mask used for comparison.\n WPUnit: the index of the watchpoint unit.\n ' + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_uint32), + ( + 'Handle', ctypes.c_uint32), + ( + 'Addr', ctypes.c_uint32), + ( + 'AddrMask', ctypes.c_uint32), + ( + 'Data', ctypes.c_uint32), + ( + 'DataMask', ctypes.c_uint32), + ( + 'Ctrl', ctypes.c_uint32), + ( + 'CtrlMask', ctypes.c_uint32), + ( + 'WPUnit', ctypes.c_uint8)] + + def __init__(self): + super(JLinkWatchpointInfo, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a formatted string describing the watchpoint. + + Args: + self (JLinkWatchpointInfo): the ``JLinkWatchpointInfo`` instance + + Returns: + String representation of the watchpoint. + """ + return self.__str__() + + def __str__(self): + """Returns a formatted string describing the watchpoint. + + Args: + self (JLinkWatchpointInfo): the ``JLinkWatchpointInfo`` instance + + Returns: + String representation of the watchpoint. + """ + name = self.__class__.__name__ + return '%s(Handle %d, Address %d)' % (name, self.Handle, self.Addr) + + +class JLinkStraceEventInfo(ctypes.Structure): + __doc__ = 'Class representing the STRACE event information.\n\n Attributes:\n SizeOfStruct: size of the structure.\n Type: type of event.\n Op: the STRACE operation to perform.\n AccessSize: access width for trace events.\n Reserved0: reserved.\n Addr: specifies the load/store address for data.\n Data: the data to be compared for the operation for data access events.\n DataMask: bitmask for bits of data to omit in comparision for data access\n events.\n AddrRangeSize: address range for range events.\n ' + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_uint32), + ( + 'Type', ctypes.c_uint8), + ( + 'Op', ctypes.c_uint8), + ( + 'AccessSize', ctypes.c_uint8), + ( + 'Reserved0', ctypes.c_uint8), + ( + 'Addr', ctypes.c_uint64), + ( + 'Data', ctypes.c_uint64), + ( + 'DataMask', ctypes.c_uint64), + ( + 'AddrRangeSize', ctypes.c_uint32)] + + def __init__(self): + super(JLinkStraceEventInfo, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a formatted string describing the event info. + + Args: + self (JLinkStraceEventInfo): the ``JLinkStraceEventInfo`` instance + + Returns: + String representation of the event info. + """ + return self.__str__() + + def __str__(self): + """Returns a formatted string describing the event info. + + Args: + self (JLinkStraceEventInfo): the ``JLinkStraceEventInfo`` instance + + Returns: + String representation of the event information. + """ + name = self.__class__.__name__ + return '%s(Type=%d, Op=%d)' % (name, self.Type, self.Op) + + +class JLinkTraceData(ctypes.Structure): + __doc__ = 'Structure representing trace data returned by the trace buffer.\n\n Attributes:\n PipeStat: type of trace data.\n Sync: sync point in buffer.\n Packet: trace data packet.\n ' + _fields_ = [ + ( + 'PipeStat', ctypes.c_uint8), + ( + 'Sync', ctypes.c_uint8), + ( + 'Packet', ctypes.c_uint16)] + + def __repr__(self): + """Returns a string representation of the trace data instance. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + A string representation of the instance. + """ + return self.__str__() + + def __str__(self): + """Returns a string representation of the trace data instance. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + A string representation of the instance. + """ + return '%s(%d)' % (self.__class__.__name__, self.Packet) + + def instruction(self): + """Returns whether the data corresponds to an executed instruction. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for an executed instruction. + """ + return self.PipeStat == 0 + + def data_instruction(self): + """Returns whether the data corresponds to an data instruction. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for an data instruction. + """ + return self.PipeStat == 1 + + def non_instruction(self): + """Returns whether the data corresponds to an un-executed instruction. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for an un-executed instruction. + """ + return self.PipeStat == 2 + + def wait(self): + """Returns whether the data corresponds to a wait. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for a wait. + """ + return self.PipeStat == 3 + + def branch(self): + """Returns whether the data corresponds to a branch execution. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for a branch execution. + """ + return self.PipeStat == 4 + + def data_branch(self): + """Returns whether the data corresponds to a branch with data. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for a branch with data. + """ + return self.PipeStat == 5 + + def trigger(self): + """Returns whether the data corresponds to a trigger event. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for a trigger event. + """ + return self.PipeStat == 6 + + def trace_disabled(self): + """Returns whether the data corresponds to trace being disabled. + + Args: + self (JLinkTraceData): the ``JLinkTraceData`` instance. + + Returns: + ``True`` if this is trace data for the trace disabled event. + """ + return self.PipeStat == 7 + + +class JLinkTraceRegion(ctypes.Structure): + __doc__ = 'Structure describing a trace region.\n\n Attributes:\n SizeOfStruct: size of the structure.\n RegionIndex: index of the region.\n NumSamples: number of samples in the region.\n Off: offset in the trace buffer.\n RegionCnt: number of trace regions.\n Dummy: unused.\n Timestamp: timestamp of last event written to buffer.\n ' + _fields_ = [ + ( + 'SizeOfStruct', ctypes.c_uint32), + ( + 'RegionIndex', ctypes.c_uint32), + ( + 'NumSamples', ctypes.c_uint32), + ( + 'Off', ctypes.c_uint32), + ( + 'RegionCnt', ctypes.c_uint32), + ( + 'Dummy', ctypes.c_uint32), + ( + 'Timestamp', ctypes.c_uint64)] + + def __init__(self): + super(JLinkTraceRegion, self).__init__() + self.SizeOfStruct = ctypes.sizeof(self) + + def __repr__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkTraceRegion): the ``JLinkTraceRegion`` instance. + + Returns: + String representation of the trace region. + """ + return self.__str__() + + def __str__(self): + """Returns a string representation of the instance. + + Args: + self (JLinkTraceRegion): the ``JLinkTraceRegion`` instance. + + Returns: + String representation of the trace region. + """ + return '%s(Index=%d)' % (self.__class__.__name__, self.RegionIndex) +# okay decompiling ./pylink/structs.pyc diff --git a/pylink/threads.py b/pylink/threads.py new file mode 100644 index 0000000..0c20d17 --- /dev/null +++ b/pylink/threads.py @@ -0,0 +1,45 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/threads.py +import threading + +class ThreadReturn(threading.Thread): + __doc__ = 'Implementation of a thread with a return value.\n\n See also:\n `StackOverflow `__.\n ' + + def __init__(self, daemon=False, *args, **kwargs): + """Initializes the thread. + + Args: + self (ThreadReturn): the ``ThreadReturn`` instance + daemon (bool): if the thread should be spawned as a daemon + args: optional list of arguments + kwargs: optional key-word arguments + + Returns: + ``None`` + """ + (super(ThreadReturn, self).__init__)(*args, **kwargs) + self.daemon = daemon + self._return = None + + def run(self): + """Runs the thread. + + Args: + self (ThreadReturn): the ``ThreadReturn`` instance + + Returns: + ``None`` + """ + target = getattr(self, '_Thread__target', getattr(self, '_target', None)) + args = getattr(self, '_Thread__args', getattr(self, '_args', None)) + kwargs = getattr(self, '_Thread__kwargs', getattr(self, '_kwargs', None)) + if target is not None: + self._return = target(*args, **kwargs) + + def join(self, *args, **kwargs): + (super(ThreadReturn, self).join)(*args, **kwargs) + return self._return +# okay decompiling ./pylink/threads.pyc diff --git a/pylink/util.py b/pylink/util.py new file mode 100644 index 0000000..b1654cb --- /dev/null +++ b/pylink/util.py @@ -0,0 +1,157 @@ +# decompyle3 version 3.9.0 +# Python bytecode version base 3.7.0 (3394) +# Decompiled from: Python 3.7.16 (default, Mar 30 2023, 01:25:49) +# [GCC 12.2.1 20220924] +# Embedded file name: pylink/util.py +from . import enums +import platform, sys + +def is_integer(val): + """Returns whether the given value is an integer. + + Args: + val (object): value to check + + Returns: + ``True`` if the given value is an integer, otherwise ``False``. + """ + try: + val += 1 + except TypeError: + return False + else: + return True + + +def is_natural(val): + """Returns whether the given value is a natrual number. + + Args: + val (object): value to check + + Returns: + ``True`` if the given value is a natural number, otherwise ``False``. + """ + return is_integer(val) and val >= 0 + + +def is_os_64bit(): + """Returns whether the current running platform is 64bit. + + Returns: + ``True`` if the platform is 64bit, otherwise ``False``. + """ + return platform.machine().endswith('64') + + +def noop(*args, **kwargs): + """No-op. Does nothing. + + Args: + args: list of arguments + kwargs: keyword arguments dictionary + + Returns: + ``None`` + """ + pass + + +def unsecure_hook_dialog(title, msg, flags): + """No-op that ignores the dialog. + + Args: + title (str): title of the unsecure dialog + msg (str): text of the unsecure dialog + flags (int): flags specifying which values can be returned + + Returns: + ``enums.JLinkFlags.DLG_BUTTON_NO`` + """ + return enums.JLinkFlags.DLG_BUTTON_NO + + +def progress_bar(iteration, total, prefix=None, suffix=None, decs=1, length=100): + """Creates a console progress bar. + + This should be called in a loop to create a progress bar. + + See `StackOverflow `__. + + Args: + iteration (int): current iteration + total (int): total iterations + prefix (str): prefix string + suffix (str): suffix string + decs (int): positive number of decimals in percent complete + length (int): character length of the bar + + Returns: + ``None`` + + Note: + This function assumes that nothing else is printed to the console in the + interim. + """ + if prefix is None: + prefix = '' + if suffix is None: + suffix = '' + format_str = '{0:.' + str(decs) + 'f}' + percents = format_str.format(100 * (iteration / float(total))) + filled_length = int(round(length * iteration / float(total))) + bar = '█' * filled_length + '-' * (length - filled_length) + prefix, suffix = prefix.strip(), suffix.strip() + sys.stdout.write('\r%s |%s| %s%s %s' % (prefix, bar, percents, '%', suffix)) + sys.stdout.flush() + if iteration == total: + sys.stdout.write('\n') + sys.stdout.flush() + + +def flash_progress_callback(action, progress_string, percentage): + """Callback that can be used with ``JLink.flash()``. + + This callback generates a progress bar in the console to show the progress + of each of the steps of the flash. + + Args: + action (str): the current action being invoked + progress_string (str): the current step in the progress + percentage (int): the percent to which the current step has been done + + Returns: + ``None`` + + Note: + This function ignores the compare action. + """ + if action.lower() != 'compare': + return progress_bar((min(100, percentage)), 100, prefix=action) + + +def calculate_parity(n): + """Calculates and returns the parity of a number. + + The parity of a number is ``1`` if the number has an odd number of ones + in its binary representation, otherwise ``0``. + + Args: + n (int): the number whose parity to calculate + + Returns: + ``1`` if the number has an odd number of ones, otherwise ``0``. + + Raises: + ValueError: if ``n`` is less than ``0``. + """ + if not is_natural(n): + raise ValueError('Expected n to be a positive integer.') + y = 0 + n = abs(n) + while n: + y += n & 1 + n = n >> 1 + + return y & 1 +# okay decompiling ./pylink/util.pyc diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..bcce11a --- /dev/null +++ b/requirements.txt @@ -0,0 +1 @@ +psutil==5.9.5