1# SPDX-FileCopyrightText: 2014-2022 Fredrik Ahlberg, Angus Gratton, 2# Espressif Systems (Shanghai) CO LTD, other contributors as noted. 3# 4# SPDX-License-Identifier: GPL-2.0-or-later 5 6import os 7import re 8import struct 9import sys 10 11 12def byte(bitstr, index): 13 return bitstr[index] 14 15 16def mask_to_shift(mask): 17 """Return the index of the least significant bit in the mask""" 18 shift = 0 19 while mask & 0x1 == 0: 20 shift += 1 21 mask >>= 1 22 return shift 23 24 25def div_roundup(a, b): 26 """Return a/b rounded up to nearest integer, 27 equivalent result to int(math.ceil(float(int(a)) / float(int(b))), only 28 without possible floating point accuracy errors. 29 """ 30 return (int(a) + int(b) - 1) // int(b) 31 32 33def flash_size_bytes(size): 34 """Given a flash size of the type passed in args.flash_size 35 (ie 512KB or 1MB) then return the size in bytes. 36 """ 37 if size is None: 38 return None 39 if "MB" in size: 40 return int(size[: size.index("MB")]) * 1024 * 1024 41 elif "KB" in size: 42 return int(size[: size.index("KB")]) * 1024 43 else: 44 raise FatalError("Unknown size %s" % size) 45 46 47def hexify(s, uppercase=True): 48 format_str = "%02X" if uppercase else "%02x" 49 return "".join(format_str % c for c in s) 50 51 52def pad_to(data, alignment, pad_character=b"\xFF"): 53 """Pad to the next alignment boundary""" 54 pad_mod = len(data) % alignment 55 if pad_mod != 0: 56 data += pad_character * (alignment - pad_mod) 57 return data 58 59 60def print_overwrite(message, last_line=False): 61 """Print a message, overwriting the currently printed line. 62 63 If last_line is False, don't append a newline at the end 64 (expecting another subsequent call will overwrite this one.) 65 66 After a sequence of calls with last_line=False, call once with last_line=True. 67 68 If output is not a TTY (for example redirected a pipe), 69 no overwriting happens and this function is the same as print(). 70 """ 71 if hasattr(sys.stdout, "isatty") and sys.stdout.isatty(): 72 print("\r%s" % message, end="\n" if last_line else "") 73 else: 74 print(message) 75 76 77def expand_chip_name(chip_name): 78 """Change chip name to official form, e.g. `esp32s3beta2` -> `ESP32-S3(beta2)`""" 79 # Put "-" after "esp32" 80 chip_name = re.sub(r"(esp32)(?!$)", r"\1-", chip_name) 81 # Put "()" around "betaN" 82 chip_name = re.sub(r"(beta\d*)", r"(\1)", chip_name) 83 # Uppercase everything before "(betaN)" 84 chip_name = re.sub(r"^[^\(]+", lambda x: x.group(0).upper(), chip_name) 85 return chip_name 86 87 88def strip_chip_name(chip_name): 89 """Strip chip name to normalized form, e.g. `ESP32-S3(beta2)` -> `esp32s3beta2`""" 90 return re.sub(r"[-()]", "", chip_name.lower()) 91 92 93def get_file_size(path_to_file): 94 """Returns the file size in bytes""" 95 file_size = 0 96 with open(path_to_file, "rb") as f: 97 f.seek(0, os.SEEK_END) 98 file_size = f.tell() 99 return file_size 100 101 102class PrintOnce: 103 """ 104 Class for printing messages just once. Can be useful when running in a loop 105 """ 106 107 def __init__(self) -> None: 108 self.already_printed = False 109 110 def __call__(self, text) -> None: 111 if not self.already_printed: 112 print(text) 113 self.already_printed = True 114 115 116class FatalError(RuntimeError): 117 """ 118 Wrapper class for runtime errors that aren't caused by internal bugs, but by 119 ESP ROM responses or input content. 120 """ 121 122 def __init__(self, message): 123 RuntimeError.__init__(self, message) 124 125 @staticmethod 126 def WithResult(message, result): 127 """ 128 Return a fatal error object that appends the hex values of 129 'result' and its meaning as a string formatted argument. 130 """ 131 132 err_defs = { 133 # ROM error codes 134 0x101: "Out of memory", 135 0x102: "Invalid argument", 136 0x103: "Invalid state", 137 0x104: "Invalid size", 138 0x105: "Requested resource not found", 139 0x106: "Operation or feature not supported", 140 0x107: "Operation timed out", 141 0x108: "Received response was invalid", 142 0x109: "CRC or checksum was invalid", 143 0x10A: "Version was invalid", 144 0x10B: "MAC address was invalid", 145 0x6001: "Flash operation failed", 146 0x6002: "Flash operation timed out", 147 0x6003: "Flash not initialised properly", 148 0x6004: "Operation not supported by the host SPI bus", 149 0x6005: "Operation not supported by the flash chip", 150 0x6006: "Can't write, protection enabled", 151 # Flasher stub error codes 152 0xC000: "Bad data length", 153 0xC100: "Bad data checksum", 154 0xC200: "Bad blocksize", 155 0xC300: "Invalid command", 156 0xC400: "Failed SPI operation", 157 0xC500: "Failed SPI unlock", 158 0xC600: "Not in flash mode", 159 0xC700: "Inflate error", 160 0xC800: "Not enough data", 161 0xC900: "Too much data", 162 0xFF00: "Command not implemented", 163 } 164 165 err_code = struct.unpack(">H", result[:2]) 166 message += " (result was {}: {})".format( 167 hexify(result), err_defs.get(err_code[0], "Unknown result") 168 ) 169 return FatalError(message) 170 171 172class NotImplementedInROMError(FatalError): 173 """ 174 Wrapper class for the error thrown when a particular ESP bootloader function 175 is not implemented in the ROM bootloader. 176 """ 177 178 def __init__(self, bootloader, func): 179 FatalError.__init__( 180 self, 181 "%s ROM does not support function %s." 182 % (bootloader.CHIP_NAME, func.__name__), 183 ) 184 185 186class NotSupportedError(FatalError): 187 def __init__(self, esp, function_name): 188 FatalError.__init__( 189 self, 190 f"{function_name} is not supported by {esp.CHIP_NAME}.", 191 ) 192 193 194class UnsupportedCommandError(RuntimeError): 195 """ 196 Wrapper class for when ROM loader returns an invalid command response. 197 198 Usually this indicates the loader is running in Secure Download Mode. 199 """ 200 201 def __init__(self, esp, op): 202 if esp.secure_download_mode: 203 msg = "This command (0x%x) is not supported in Secure Download Mode" % op 204 else: 205 msg = "Invalid (unsupported) command 0x%x" % op 206 RuntimeError.__init__(self, msg) 207