diff options
Diffstat (limited to 'mule-attiny/i2c-flash/src')
-rwxr-xr-x | mule-attiny/i2c-flash/src/mule-attiny_i2c_flash/i2c_flash.py | 96 |
1 files changed, 96 insertions, 0 deletions
diff --git a/mule-attiny/i2c-flash/src/mule-attiny_i2c_flash/i2c_flash.py b/mule-attiny/i2c-flash/src/mule-attiny_i2c_flash/i2c_flash.py index c867558..7beea78 100755 --- a/mule-attiny/i2c-flash/src/mule-attiny_i2c_flash/i2c_flash.py +++ b/mule-attiny/i2c-flash/src/mule-attiny_i2c_flash/i2c_flash.py @@ -11,6 +11,9 @@ from time import sleep from argparse import ArgumentParser import pathlib import logging +from abc import abstractmethod +from enum import IntEnum + # Bootloader I2C slave address BL_I2C_DEV = 0x6f # Reuse isl1208 address @@ -82,6 +85,83 @@ class BootModeController: raise Exception("UPDI Reset command failed!") +class BLHdrID(IntEnum): + VERSION = 0 + # Add other bootloader header IDs here + + +def get_blhdr(blhdr): + id = (blhdr & (0b111 << 5)) + if id == BLHdrID.VERSION: + return BLVersionHdr(blhdr) + # Add other bootloader header IDs here + raise Exception(f"Unknown {id} ID for payload header") + + +class BLHdr(): + def __init__(self, hdr): + self.hdr = hdr + + @abstractmethod + def len(self): + """Returns length of payload excluding header""" + pass + + @abstractmethod + def payload_class(self): + """Returns the class to use for representing the data following the header""" + pass + + +class BLVersionHdr(BLHdr): + DIRTY_BIT_p = 4 + DIRTY_BIT_m = (0b1 << DIRTY_BIT_p) + LAST_HASH_CHAR_p = 0 + LAST_HASH_CHAR_m = (0xf << LAST_HASH_CHAR_p) + + def len(self): + """1B for MAJOR + 1B for MINOR + 1B for PATCH + 1B for NCOMMITS + 3B for 6 hex digits of the hash""" + return 7 + + def payload_class(self): + return BLVersion + + def is_dirty(self): + return bool(self.hdr & BLVersionHdr.DIRTY_BIT_m) + + def last_hash(self): + return self.hdr & BLVersionHdr.LAST_HASH_CHAR_m + + +class BLVersion(): + def __init__(self, header, payload): + self.major, self.minor, self.patch, self.ncommits, hash_12, hash_34, hash_56 = payload + self.header = header + self.dirty = self.header.is_dirty() + self.hash = f"{hash_12:02x}{hash_34:02x}{hash_56:02x}{self.header.last_hash():0x}" + + def __str__(self): + return f"Bootloader v{self.major}.{self.minor}.{self.patch}-{self.ncommits}-g{self.hash}{'-dirty' if self.dirty else ''} is online" # noqa: E501 + + +class BLParser(): + @classmethod + def parse(cls, payload): + if not payload: + logging.error("No bootloader data is available!") + return + cursor = 0 + while cursor < len(payload): + blhdr = get_blhdr(payload[cursor]) + clss = blhdr.payload_class() + cursor += 1 + if len(payload) < cursor + blhdr.len(): + logging.error(f"Invalid data header of type {type(blhdr).__name__}") + return + yield clss(blhdr, payload[cursor:cursor + blhdr.len()]) + cursor += blhdr.len() + + def crc16_xmodem(data): # Credits: https://gist.github.com/oysstu/68072c44c02879a2abf94ef350d1c7c6?permalink_comment_id=3943460#gistcomment-3943460 # noqa: E501 ''' @@ -190,6 +270,22 @@ def main(): try: # Request flashloader through bootloader (Set to read-only-mode by default) bootCtrl.enterFlashloader() + + bus = SMBus(args.i2c_bus) + bl_raw_data = [] + while True: + try: + # Read bl_data byte per byte + bl_raw_data.append(bus.read_byte(BL_I2C_DEV)) + except IOError: + # Bootloader finished sending bl_data + break + + bl_data = BLParser.parse(bl_raw_data) + + for d in bl_data: + logging.info(d) + bootCtrl.exitRoMode() ret = fw_flash(_bin, args.i2c_bus) |