Explorar o código

[FL-1443, FL-1289] Move assets compilation to separate Makefile. Add scripts folder. Add OTP flashing with DFU. (#531)

* Assets: move assets compilation to separate Makefile. Move all scripts to scripts folder. Add scripts ReadMe. Add precompiled assets.
* Split assets.py into separate entities. Option bytes for FL-1289 and checker/setter.
* Cli: explicitly initialize variable befor use in api_hal_vcp_rx_with_timeout
* Rename ob_check script to ob.
あく %!s(int64=4) %!d(string=hai) anos
pai
achega
8116bfcbab

+ 1 - 1
applications/cli/cli.c

@@ -51,7 +51,7 @@ size_t cli_read(Cli* cli, uint8_t* buffer, size_t size) {
 }
 }
 
 
 bool cli_cmd_interrupt_received(Cli* cli) {
 bool cli_cmd_interrupt_received(Cli* cli) {
-    char c;
+    char c = '\0';
     api_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1);
     api_hal_vcp_rx_with_timeout((uint8_t*)&c, 1, 1);
     return c == CliSymbolAsciiETX;
     return c == CliSymbolAsciiETX;
 }
 }

+ 11 - 0
assets/Makefile

@@ -0,0 +1,11 @@
+PROJECT_ROOT		= $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST))))..)
+
+include				$(PROJECT_ROOT)/assets/assets.mk
+
+$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
+	@echo "\tASSETS\t" $@
+	@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_COMPILED_DIR)
+
+clean:
+	@echo "\tCLEAN\t"
+	@$(RM) $(ASSETS)

+ 10 - 0
assets/ReadMe.md

@@ -1,3 +1,13 @@
+# Requirements
+
+- Python3
+- ImageMagic
+- Make
+
+# Compiling
+
+	make all
+
 # Asset naming rules
 # Asset naming rules
 
 
 ## Images and Animations
 ## Images and Animations

+ 5 - 5
assets/assets.mk

@@ -1,10 +1,10 @@
 ASSETS_DIR			:= $(PROJECT_ROOT)/assets
 ASSETS_DIR			:= $(PROJECT_ROOT)/assets
-ASSETS_COMPILLER	:= $(ASSETS_DIR)/assets.py
-ASSETS_OUTPUT_DIR	:= $(ASSETS_DIR)/output
+ASSETS_COMPILLER	:= $(PROJECT_ROOT)/scripts/assets.py
+ASSETS_COMPILED_DIR	:= $(ASSETS_DIR)/compiled
 ASSETS_SOURCE_DIR	:= $(ASSETS_DIR)/icons
 ASSETS_SOURCE_DIR	:= $(ASSETS_DIR)/icons
 
 
 ASSETS_SOURCES		+= $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate')
 ASSETS_SOURCES		+= $(shell find $(ASSETS_SOURCE_DIR) -type f -iname '*.png' -or -iname 'frame_rate')
-ASSETS				+= $(ASSETS_OUTPUT_DIR)/assets_icons.c
+ASSETS				+= $(ASSETS_COMPILED_DIR)/assets_icons.c
 
 
-CFLAGS				+= -I$(ASSETS_OUTPUT_DIR)
-C_SOURCES			+= $(ASSETS_OUTPUT_DIR)/assets_icons.c
+CFLAGS				+= -I$(ASSETS_COMPILED_DIR)
+C_SOURCES			+= $(ASSETS_COMPILED_DIR)/assets_icons.c

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 48 - 0
assets/compiled/assets_icons.c


+ 131 - 0
assets/compiled/assets_icons.h

@@ -0,0 +1,131 @@
+#pragma once
+
+#include <gui/icon.h>
+
+typedef enum {
+	I_SDQuestion_35x43,
+	I_SDError_43x35,
+	I_Health_16x16,
+	I_FaceCharging_29x14,
+	I_BatteryBody_52x28,
+	I_Voltage_16x16,
+	I_Temperature_16x16,
+	I_FaceNopower_29x14,
+	I_FaceNormal_29x14,
+	I_Battery_16x16,
+	I_FaceConfused_29x14,
+	I_PassportBottom_128x17,
+	I_DoorLeft_8x56,
+	I_DoorLocked_10x56,
+	I_DoorRight_8x56,
+	I_DoorLeft_70x55,
+	I_PassportLeft_6x47,
+	I_DoorRight_70x55,
+	I_LockPopup_100x49,
+	I_WalkR2_32x32,
+	I_WalkL2_32x32,
+	I_WalkRB1_32x32,
+	I_Home_painting_17x20,
+	I_WalkLB2_32x32,
+	I_Sofa_40x13,
+	I_WalkLB1_32x32,
+	I_PC_22x29,
+	I_WalkL1_32x32,
+	I_TV_20x20,
+	I_WalkR1_32x32,
+	I_WalkRB2_32x32,
+	I_TV_20x24,
+	I_dir_10px,
+	I_Nfc_10px,
+	I_sub1_10px,
+	I_ir_10px,
+	I_ibutt_10px,
+	I_unknown_10px,
+	I_ble_10px,
+	I_125_10px,
+	I_FX_SittingB_40x27,
+	I_BigGames_24x24,
+	I_BigProfile_24x24,
+	I_DolphinOkay_41x43,
+	I_DolphinFirstStart5_45x53,
+	I_DolphinFirstStart4_67x53,
+	I_DolphinFirstStart2_59x51,
+	I_DolphinFirstStart0_70x53,
+	I_DolphinFirstStart6_58x54,
+	I_DolphinFirstStart1_59x53,
+	I_DolphinFirstStart8_56x51,
+	I_DolphinFirstStart7_61x51,
+	I_Flipper_young_80x60,
+	I_BigBurger_24x24,
+	I_FX_Bang_32x6,
+	I_DolphinFirstStart3_57x48,
+	I_BadUsb_9x8,
+	I_PlaceholderR_30x13,
+	I_Background_128x8,
+	I_Lock_8x8,
+	I_Battery_26x8,
+	I_PlaceholderL_11x13,
+	I_Battery_19x8,
+	I_SDcardMounted_11x8,
+	I_SDcardFail_11x8,
+	I_USBConnected_15x8,
+	I_Bluetooth_5x8,
+	I_Background_128x11,
+	I_IrdaArrowUp_4x8,
+	I_IrdaLearnShort_128x31,
+	I_IrdaArrowDown_4x8,
+	I_IrdaLearn_128x64,
+	I_IrdaSend_128x64,
+	I_IrdaSendShort_128x34,
+	I_passport_happy1_43x45,
+	I_passport_bad3_43x45,
+	I_passport_okay2_43x45,
+	I_passport_bad2_43x45,
+	I_passport_okay3_43x45,
+	I_passport_bad1_43x45,
+	I_passport_happy3_43x45,
+	I_passport_happy2_43x45,
+	I_passport_okay1_43x45,
+	I_ButtonRightSmall_3x5,
+	I_ButtonLeft_4x7,
+	I_ButtonLeftSmall_3x5,
+	I_ButtonRight_4x7,
+	I_ButtonCenter_7x7,
+	A_Games_14,
+	A_Plugins_14,
+	A_Passport_14,
+	A_Sub1ghz_14,
+	A_NFC_14,
+	A_Tamagotchi_14,
+	A_FileManager_14,
+	A_125khz_14,
+	A_U2F_14,
+	A_Infrared_14,
+	A_Power_14,
+	A_Settings_14,
+	A_iButton_14,
+	A_Bluetooth_14,
+	A_GPIO_14,
+	I_DolphinMafia_115x62,
+	I_DolphinExcited_64x63,
+	I_iButtonDolphinSuccess_109x60,
+	I_iButtonDolphinVerySuccess_108x52,
+	I_iButtonKey_49x44,
+	I_DolphinNice_96x59,
+	I_DolphinWait_61x59,
+	A_Wink_128x64,
+	A_MDWL_32x32,
+	A_MDWR_32x32,
+	A_WatchingTV_128x64,
+	A_MDI_32x32,
+	A_MDWRB_32x32,
+	A_MDIB_32x32,
+	A_FX_Sitting_40x27,
+	A_MDWLB_32x32,
+	I_KeySave_24x11,
+	I_KeyBackspaceSelected_16x9,
+	I_KeySaveSelected_24x11,
+	I_KeyBackspace_16x9,
+} IconName;
+
+Icon * assets_icons_get(IconName name);

+ 5 - 0
assets/compiled/assets_icons_i.h

@@ -0,0 +1,5 @@
+#pragma once
+
+#include <assets_icons.h>
+
+const IconData * assets_icons_get_data(IconName name);

+ 0 - 1
assets/output/.gitignore

@@ -1 +0,0 @@
-*

+ 1 - 1
bootloader/Makefile

@@ -8,7 +8,7 @@ ASM_SOURCES		+= $(wildcard src/*.s)
 C_SOURCES		+= $(wildcard src/*.c)
 C_SOURCES		+= $(wildcard src/*.c)
 CPP_SOURCES		+= $(wildcard src/*.cpp)
 CPP_SOURCES		+= $(wildcard src/*.cpp)
 
 
-TARGET			?= f5
+TARGET			?= f6
 TARGET_DIR		= targets/$(TARGET)
 TARGET_DIR		= targets/$(TARGET)
 include			$(TARGET_DIR)/target.mk
 include			$(TARGET_DIR)/target.mk
 
 

+ 2 - 1
firmware/Makefile

@@ -3,6 +3,7 @@ PROJECT			= firmware
 
 
 include 		$(PROJECT_ROOT)/make/base.mk
 include 		$(PROJECT_ROOT)/make/base.mk
 include			$(PROJECT_ROOT)/assets/assets.mk
 include			$(PROJECT_ROOT)/assets/assets.mk
+CFLAGS			+= -I$(ASSETS_COMPILED_DIR)
 include			$(PROJECT_ROOT)/core/core.mk
 include			$(PROJECT_ROOT)/core/core.mk
 include 		$(PROJECT_ROOT)/applications/applications.mk
 include 		$(PROJECT_ROOT)/applications/applications.mk
 include			$(PROJECT_ROOT)/lib/lib.mk
 include			$(PROJECT_ROOT)/lib/lib.mk
@@ -10,7 +11,7 @@ include			$(PROJECT_ROOT)/lib/lib.mk
 CFLAGS += -Werror -Wno-address-of-packed-member
 CFLAGS += -Werror -Wno-address-of-packed-member
 CPPFLAGS += -Werror
 CPPFLAGS += -Werror
 
 
-TARGET			?= f5
+TARGET			?= f6
 
 
 TARGET_DIR		= targets/$(TARGET)
 TARGET_DIR		= targets/$(TARGET)
 
 

+ 0 - 9
flash_core1_main.sh

@@ -1,9 +0,0 @@
-#!/bin/bash
-
-set -x -e
-
-rm bootloader/.obj/f*/flash || true
-make -C bootloader -j9 flash
-
-rm firmware/.obj/f*/flash || true
-make -C firmware -j9 flash

+ 4 - 9
make/rules.mk

@@ -47,15 +47,15 @@ $(OBJ_DIR)/$(PROJECT).bin: $(OBJ_DIR)/$(PROJECT).elf
 	@echo "\tBIN\t" $@
 	@echo "\tBIN\t" $@
 	@$(BIN) $< $@
 	@$(BIN) $< $@
 
 
-$(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
+$(OBJ_DIR)/%.o: %.c $(OBJ_DIR)/BUILD_FLAGS
 	@echo "\tCC\t" $< "->" $@
 	@echo "\tCC\t" $< "->" $@
 	@$(CC) $(CFLAGS) -c $< -o $@
 	@$(CC) $(CFLAGS) -c $< -o $@
 
 
-$(OBJ_DIR)/%.o: %.s $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
+$(OBJ_DIR)/%.o: %.s $(OBJ_DIR)/BUILD_FLAGS
 	@echo "\tASM\t" $< "->" $@
 	@echo "\tASM\t" $< "->" $@
 	@$(AS) $(CFLAGS) -c $< -o $@
 	@$(AS) $(CFLAGS) -c $< -o $@
 
 
-$(OBJ_DIR)/%.o: %.cpp $(OBJ_DIR)/BUILD_FLAGS $(ASSETS)
+$(OBJ_DIR)/%.o: %.cpp $(OBJ_DIR)/BUILD_FLAGS
 	@echo "\tCPP\t" $< "->" $@
 	@echo "\tCPP\t" $< "->" $@
 	@$(CPP) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
 	@$(CPP) $(CFLAGS) $(CPPFLAGS) -c $< -o $@
 
 
@@ -67,10 +67,6 @@ $(OBJ_DIR)/upload: $(OBJ_DIR)/$(PROJECT).bin
 	dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS)
 	dfu-util -D $(OBJ_DIR)/$(PROJECT).bin -a 0 -s $(FLASH_ADDRESS) $(DFU_OPTIONS)
 	touch $@
 	touch $@
 
 
-$(ASSETS): $(ASSETS_SOURCES) $(ASSETS_COMPILLER)
-	@echo "\tASSETS\t" $@
-	@$(ASSETS_COMPILLER) icons -s $(ASSETS_SOURCE_DIR) -o $(ASSETS_OUTPUT_DIR)
-
 flash: $(OBJ_DIR)/flash
 flash: $(OBJ_DIR)/flash
 
 
 upload: $(OBJ_DIR)/upload
 upload: $(OBJ_DIR)/upload
@@ -104,7 +100,6 @@ bm_debug: flash
 clean:
 clean:
 	@echo "\tCLEAN\t"
 	@echo "\tCLEAN\t"
 	@$(RM) $(OBJ_DIR)/*
 	@$(RM) $(OBJ_DIR)/*
-	@$(RM) $(ASSETS)
 
 
 z: clean
 z: clean
 	$(MAKE) all
 	$(MAKE) all
@@ -123,7 +118,7 @@ format:
 	@echo "Formatting sources with clang-format"
 	@echo "Formatting sources with clang-format"
 	@clang-format -style=file -i $(FORMAT_SOURCES)
 	@clang-format -style=file -i $(FORMAT_SOURCES)
 
 
-generate_cscope_db: $(ASSETS)
+generate_cscope_db:
 	@echo "$(C_SOURCES) $(CPP_SOURCES) $(ASM_SOURCES)" | tr ' ' '\n' > $(OBJ_DIR)/source.list.p
 	@echo "$(C_SOURCES) $(CPP_SOURCES) $(ASM_SOURCES)" | tr ' ' '\n' > $(OBJ_DIR)/source.list.p
 	@cat ~/headers.list >> $(OBJ_DIR)/source.list.p
 	@cat ~/headers.list >> $(OBJ_DIR)/source.list.p
 	@cat $(OBJ_DIR)/source.list.p | sed -e "s|^[^//]|$$PWD/&|g" > $(OBJ_DIR)/source.list
 	@cat $(OBJ_DIR)/source.list.p | sed -e "s|^[^//]|$$PWD/&|g" > $(OBJ_DIR)/source.list

+ 51 - 0
scripts/ReadMe.md

@@ -0,0 +1,51 @@
+# About
+
+This folder contains differnt scripts that automates routine actions.
+Flashing scripts are based on cli version of [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html).
+You will need to add STM32_Programmer_CLI to your path to use them.
+
+# Flashing empty MCU/Flipper
+
+Always flash your device in the folllowing sequence:
+
+- OTP (Only on empty MCU)
+- Core2 firmware
+- Core1 firmware
+- Option Bytes
+
+## Otp flashing
+
+!!! Flashing incorrect OTP may permanently brick your device !!!
+
+Normally OTP data generated and flashed at factory.
+In case if MCU was replaced you'll need correct OTP data to be able to use companion applications.
+Use `otp.py` to generate OTP data and `flash_otp_version_*` to flash OTP zone.
+You will need exact main board revision to genrate OTP data. It can be found on main PCB.
+
+!!! Flashing incorrect OTP may permanently brick your device !!!
+
+## Core2 flashing
+
+Script blindly updates FUS and Radiostack. This operation is going to corrupt bootloader and firmware.
+Reflash Core1 after Core2.
+
+## Core1 flashing
+
+Script compiles and flashes both bootloader and firmware.
+
+## Option Bytes
+
+!!! Setting incorrect Otion Bytes may brick your MCU !!!
+
+Defaults are mostly OK, but there are couple things that we'd like to tune.
+Also OB may be damaged, so we've made couple scripts to check and set option bytes.
+
+!!! Setting incorrect Otion Bytes may brick your MCU !!!
+
+Checking option bytes:
+
+	ob.py check
+
+Setting option bytes:
+
+	ob.py set

+ 2 - 54
assets/assets.py → scripts/assets.py

@@ -6,9 +6,6 @@ import subprocess
 import io
 import io
 import os
 import os
 import sys
 import sys
-import re
-import struct
-import datetime
 
 
 ICONS_SUPPORTED_FORMATS = ["png"]
 ICONS_SUPPORTED_FORMATS = ["png"]
 
 
@@ -51,7 +48,7 @@ Icon * assets_icons_get(IconName name) {
 """
 """
 
 
 
 
-class Assets:
+class Main:
     def __init__(self):
     def __init__(self):
         # command args
         # command args
         self.parser = argparse.ArgumentParser()
         self.parser = argparse.ArgumentParser()
@@ -67,22 +64,6 @@ class Assets:
             "-o", "--output-directory", help="Output directory"
             "-o", "--output-directory", help="Output directory"
         )
         )
         self.parser_icons.set_defaults(func=self.icons)
         self.parser_icons.set_defaults(func=self.icons)
-        self.parser_otp = self.subparsers.add_parser(
-            "otp", help="OTP HW version generator"
-        )
-        self.parser_otp.add_argument(
-            "--version", type=int, help="Version", required=True
-        )
-        self.parser_otp.add_argument(
-            "--firmware", type=int, help="Firmware", required=True
-        )
-        self.parser_otp.add_argument("--body", type=int, help="Body", required=True)
-        self.parser_otp.add_argument(
-            "--connect", type=int, help="Connect", required=True
-        )
-        self.parser_otp.add_argument("--name", type=str, help="Name", required=True)
-        self.parser_otp.add_argument("file", help="Output file")
-        self.parser_otp.set_defaults(func=self.otp)
         # logging
         # logging
         self.logger = logging.getLogger()
         self.logger = logging.getLogger()
 
 
@@ -101,39 +82,6 @@ class Assets:
         # execute requested function
         # execute requested function
         self.args.func()
         self.args.func()
 
 
-    def otp(self):
-        self.logger.debug(f"Generating OTP")
-
-        if self.args.name:
-            name = re.sub(
-                "[^a-zA-Z0-9.]", "", self.args.name
-            )  # Filter all special characters
-            name = list(
-                map(str, map(ord, name[0:8]))
-            )  # Strip to 8 chars and map to ascii codes
-            while len(name) < 8:
-                name.append("0")
-
-            n1, n2, n3, n4, n5, n6, n7, n8 = map(int, name)
-
-        data = struct.pack(
-            "<BBBBLBBBBBBBB",
-            self.args.version,
-            self.args.firmware,
-            self.args.body,
-            self.args.connect,
-            int(datetime.datetime.now().timestamp()),
-            n1,
-            n2,
-            n3,
-            n4,
-            n5,
-            n6,
-            n7,
-            n8,
-        )
-        open(self.args.file, "wb").write(data)
-
     def icons(self):
     def icons(self):
         self.logger.debug(f"Converting icons")
         self.logger.debug(f"Converting icons")
         icons_c = open(os.path.join(self.args.output_directory, "assets_icons.c"), "w")
         icons_c = open(os.path.join(self.args.output_directory, "assets_icons.c"), "w")
@@ -248,4 +196,4 @@ class Assets:
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
-    Assets()()
+    Main()()

+ 12 - 0
scripts/flash_core1_main_swd.sh

@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -x -e
+
+SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
+
+rm $PROJECT_DIR/bootloader/.obj/f*/flash || true
+make -C $PROJECT_DIR/bootloader -j9 flash
+
+rm $PROJECT_DIR/firmware/.obj/f*/flash || true
+make -C $PROJECT_DIR/firmware -j9 flash

+ 3 - 2
flash_core2_ble.sh → scripts/flash_core2_ble_swd.sh

@@ -2,8 +2,9 @@
 
 
 set -x -e
 set -x -e
 
 
-COPRO_DIR="lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
-
+SCRIPT_DIR="$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )"
+PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
+COPRO_DIR="$PROJECT_DIR/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x"
 
 
 STM32_Programmer_CLI -c port=swd -fwupgrade $COPRO_DIR/stm32wb5x_FUS_fw_1_0_2.bin 0x080EC000 || true
 STM32_Programmer_CLI -c port=swd -fwupgrade $COPRO_DIR/stm32wb5x_FUS_fw_1_0_2.bin 0x080EC000 || true
 STM32_Programmer_CLI -c port=swd
 STM32_Programmer_CLI -c port=swd

+ 17 - 0
scripts/flash_otp_version_dfu.sh

@@ -0,0 +1,17 @@
+#!/bin/bash
+
+set -x -e
+
+if [ "$#" -ne 1 ]; then
+    echo "OTP file required"
+    exit
+fi
+
+if [ ! -f $1 ]; then
+    echo "Unable to open OTP file"
+    exit
+fi
+
+STM32_Programmer_CLI -c port=usb1 -d $1 0x1FFF7000
+
+STM32_Programmer_CLI -c port=usb1 -r8 0x1FFF7000 8

+ 0 - 0
flash_otp_version.sh → scripts/flash_otp_version_swd.sh


+ 0 - 0
flash_wipe.sh → scripts/flash_wipe_swd.sh


+ 113 - 0
scripts/ob.py

@@ -0,0 +1,113 @@
+#!/usr/bin/env python3
+
+import logging
+import argparse
+import subprocess
+import sys
+import os
+
+
+class Main:
+    def __init__(self):
+        # command args
+        self.parser = argparse.ArgumentParser()
+        self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
+        self.subparsers = self.parser.add_subparsers(help="sub-command help")
+        self.parser_check = self.subparsers.add_parser(
+            "check", help="Check Option Bytes"
+        )
+        self.parser_check.set_defaults(func=self.check)
+        self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
+        self.parser_set.set_defaults(func=self.set)
+        # logging
+        self.logger = logging.getLogger()
+        # OB
+        self.ob = {}
+
+    def __call__(self):
+        self.args = self.parser.parse_args()
+        if "func" not in self.args:
+            self.parser.error("Choose something to do")
+        # configure log output
+        self.log_level = logging.DEBUG if self.args.debug else logging.INFO
+        self.logger.setLevel(self.log_level)
+        self.handler = logging.StreamHandler(sys.stdout)
+        self.handler.setLevel(self.log_level)
+        self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
+        self.handler.setFormatter(self.formatter)
+        self.logger.addHandler(self.handler)
+        # execute requested function
+        self.loadOB()
+        self.args.func()
+
+    def loadOB(self):
+        self.logger.info(f"Loading Option Bytes data")
+        file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
+        file = open(file_path, "r")
+        for line in file.readlines():
+            k, v, o = line.split(":")
+            self.ob[k.strip()] = v.strip(), o.strip()
+
+    def check(self):
+        self.logger.info(f"Checking Option Bytes")
+        try:
+            output = subprocess.check_output(
+                ["STM32_Programmer_CLI", "-q", "-c port=swd", "-ob displ"]
+            )
+            assert output
+        except Exception as e:
+            self.logger.error(f"Failed to call STM32_Programmer_CLI")
+            self.logger.exception(e)
+            return
+        ob_correct = True
+        for line in output.decode().split("\n"):
+            line = line.strip()
+            if not ":" in line:
+                self.logger.debug(f"Skipping line: {line}")
+                continue
+            key, data = line.split(":", 1)
+            key = key.strip()
+            data = data.strip()
+            if not key in self.ob.keys():
+                self.logger.debug(f"Skipping key: {key}")
+                continue
+            self.logger.debug(f"Processing key: {key} {data}")
+            value, comment = data.split(" ", 1)
+            value = value.strip()
+            comment = comment.strip()
+            if self.ob[key][0] != value:
+                self.logger.error(
+                    f"Invalid OB: {key} {value}, expected: {self.ob[key][0]}"
+                )
+                ob_correct = False
+        if ob_correct:
+            self.logger.info(f"OB Check OK")
+        else:
+            self.logger.error(f"OB Check FAIL")
+            exit(255)
+
+    def set(self):
+        self.logger.info(f"Setting Option Bytes")
+        options = []
+        for key, (value, attr) in self.ob.items():
+            if "w" in attr:
+                options.append(f"{key}={value}")
+        try:
+            output = subprocess.check_output(
+                [
+                    "STM32_Programmer_CLI",
+                    "-q",
+                    "-c port=swd",
+                    f"-ob {' '.join(options)}",
+                ]
+            )
+            assert output
+            self.logger.info(f"Success")
+        except Exception as e:
+            self.logger.error(f"Failed to call STM32_Programmer_CLI")
+            self.logger.exception(e)
+            return
+
+
+if __name__ == "__main__":
+    Main()()

+ 34 - 0
scripts/ob_check.data

@@ -0,0 +1,34 @@
+RDP:0xAA:r
+BOR_LEV:0x4:rw
+nBOOT0:0x1:rw
+nBOOT1:0x1:rw
+nSWBOOT0:0x1:rw
+SRAM2RST:0x0:rw
+SRAM2PE:0x1:rw
+nRST_STOP:0x1:rw
+nRST_STDBY:0x1:rw
+nRSTSHDW:0x1:rw
+WWDGSW:0x1:rw
+IWGDSTDBY:0x1:rw
+IWDGSTOP:0x1:rw
+IWDGSW:0x1:rw
+IPCCDBA:0x0:rw
+ESE:0x1:r
+SFSA:0xCB:r
+FSD:0x0:r
+DDS:0x1:r
+C2OPT:0x1:r
+NBRSD:0x0:r
+SNBRSA:0xF:r
+BRSD:0x0:r
+SBRSA:0xA:r
+SBRV:0x32C00:r
+PCROP1A_STRT:0x1FF:r
+PCROP1A_END:0x0:r
+PCROP_RDP:0x1:r
+PCROP1B_STRT:0x1FF:r
+PCROP1B_END:0x0:r
+WRP1A_STRT:0xFF:r
+WRP1A_END:0x0:r
+WRP1B_STRT:0xFF:r
+WRP1B_END:0x0:r

+ 91 - 0
scripts/otp.py

@@ -0,0 +1,91 @@
+#!/usr/bin/env python3
+
+import logging
+import argparse
+import os
+import sys
+import re
+import struct
+import datetime
+
+
+class Main:
+    def __init__(self):
+        # command args
+        self.parser = argparse.ArgumentParser()
+        self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
+        self.subparsers = self.parser.add_subparsers(help="sub-command help")
+        self.parser_generate = self.subparsers.add_parser(
+            "generate", help="OTP HW version generator"
+        )
+        self.parser_generate.add_argument(
+            "--version", type=int, help="Version", required=True
+        )
+        self.parser_generate.add_argument(
+            "--firmware", type=int, help="Firmware", required=True
+        )
+        self.parser_generate.add_argument(
+            "--body", type=int, help="Body", required=True
+        )
+        self.parser_generate.add_argument(
+            "--connect", type=int, help="Connect", required=True
+        )
+        self.parser_generate.add_argument(
+            "--name", type=str, help="Name", required=True
+        )
+        self.parser_generate.add_argument("file", help="Output file")
+        self.parser_generate.set_defaults(func=self.generate)
+        # logging
+        self.logger = logging.getLogger()
+
+    def __call__(self):
+        self.args = self.parser.parse_args()
+        if "func" not in self.args:
+            self.parser.error("Choose something to do")
+        # configure log output
+        self.log_level = logging.DEBUG if self.args.debug else logging.INFO
+        self.logger.setLevel(self.log_level)
+        self.handler = logging.StreamHandler(sys.stdout)
+        self.handler.setLevel(self.log_level)
+        self.formatter = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
+        self.handler.setFormatter(self.formatter)
+        self.logger.addHandler(self.handler)
+        # execute requested function
+        self.args.func()
+
+    def generate(self):
+        self.logger.debug(f"Generating OTP")
+
+        if self.args.name:
+            name = re.sub(
+                "[^a-zA-Z0-9.]", "", self.args.name
+            )  # Filter all special characters
+            name = list(
+                map(str, map(ord, name[0:8]))
+            )  # Strip to 8 chars and map to ascii codes
+            while len(name) < 8:
+                name.append("0")
+
+            n1, n2, n3, n4, n5, n6, n7, n8 = map(int, name)
+
+        data = struct.pack(
+            "<BBBBLBBBBBBBB",
+            self.args.version,
+            self.args.firmware,
+            self.args.body,
+            self.args.connect,
+            int(datetime.datetime.now().timestamp()),
+            n1,
+            n2,
+            n3,
+            n4,
+            n5,
+            n6,
+            n7,
+            n8,
+        )
+        open(self.args.file, "wb").write(data)
+
+
+if __name__ == "__main__":
+    Main()()

+ 0 - 4
syntax_check.sh

@@ -1,4 +0,0 @@
-#!/usr/bin/env bash
-
-echo "RUN SYNTAX CHECK INSIDE CONTAINER"
-docker-compose exec dev ./docker/syntax_check.sh

Algúns arquivos non se mostraron porque demasiados arquivos cambiaron neste cambio