فهرست منبع

[FL-1970, FL-1965, FL-1872, FL-1689] Python framework, Scripts and fixes (#779)

* Scripts: add flipper lib, migrate ob to flipper lib, update ob.data
* Makefile: speedup build with phony target for .d files
* FuriHal,U8G2: full MGG display support and ERC contrast tuning.
* Desktop: fix dolphin rename artifact.
* Scripts: port otp.py to flipper scripting lib.
* Scripts: add wipe and core1 flashing to flash.py, remove obsolete shell scripts
* Scripts: replace core1 flashing script with global makefile.
* Scripts: final touches and migration to python. Root Makefile for everything.
あく 4 سال پیش
والد
کامیت
2751440193

+ 71 - 0
Makefile

@@ -0,0 +1,71 @@
+PROJECT_ROOT := $(abspath $(dir $(abspath $(firstword $(MAKEFILE_LIST)))))
+COPRO_DIR := $(PROJECT_ROOT)/lib/STM32CubeWB/Projects/STM32WB_Copro_Wireless_Binaries/STM32WB5x
+
+.PHONY: all
+all: bootloader_all firmware_all
+
+.PHONY: whole
+whole: flash_radio bootloader_flash firmware_flash
+
+.PHONY: clean
+clean: bootloader_clean firmware_clean
+
+.PHONY: flash
+flash: bootloader_flash firmware_flash
+
+.PHONY: debug
+debug:
+	$(MAKE) -C firmware -j9 debug
+
+.PHONY: wipe
+wipe:
+	$(PROJECT_ROOT)/scripts/flash.py wipe
+	$(PROJECT_ROOT)/scripts/ob.py set
+
+.PHONY: bootloader_all
+bootloader_all:
+	$(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 all
+
+.PHONY: firmware_all
+firmware_all:
+	$(MAKE) -C $(PROJECT_ROOT)/firmware -j9 all
+
+.PHONY: bootloader_clean
+bootloader_clean:
+	$(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 clean
+
+.PHONY: firmware_clean
+firmware_clean:
+	$(MAKE) -C $(PROJECT_ROOT)/firmware -j9 clean
+
+.PHONY: bootloader_flash
+bootloader_flash:
+	rm $(PROJECT_ROOT)/bootloader/.obj/f*/flash || true
+	$(MAKE) -C $(PROJECT_ROOT)/bootloader -j9 flash
+
+.PHONY: firmware_flash
+firmware_flash:
+	rm $(PROJECT_ROOT)/firmware/.obj/f*/flash || true
+	$(MAKE) -C $(PROJECT_ROOT)/firmware -j9 flash
+
+.PHONY: flash_radio
+flash_radio:
+	$(PROJECT_ROOT)/scripts/flash.py core2radio 0x080CA000 $(COPRO_DIR)/stm32wb5x_BLE_Stack_full_fw.bin
+	$(PROJECT_ROOT)/scripts/ob.py set
+
+.PHONY: flash_radio_fus
+flash_radio_fus:
+	@echo
+	@echo "================   DON'T DO IT    ================"
+	@echo "= Flashing FUS is going to erase secure enclave  ="
+	@echo "= You will loose ability to use encrypted assets ="
+	@echo "=       type 'find / -exec rm -rf {} \;'         ="
+	@echo "=     In case if you still want to continue      ="
+	@echo "================    JUST DON'T    ================"
+	@echo
+
+.PHONY: 
+flash_radio_fus_please_i_m_not_going_to_complain:
+	$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw_for_fus_0_5_3.bin
+	$(PROJECT_ROOT)/scripts/flash.py core2fus 0x080EC000 --statement=AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE $(COPRO_DIR)/stm32wb5x_FUS_fw.bin
+	$(PROJECT_ROOT)/scripts/ob.py set

+ 12 - 0
ReadMe.md

@@ -70,6 +70,18 @@ One liner: `./flash_core1_main.sh`
    docker-compose up -d
    docker-compose up -d
    ```
    ```
 
 
+## Compile everything
+
+```sh
+docker-compose exec dev make -j$(nproc)
+```
+
+## Flash everything
+
+```sh
+docker-compose exec dev make -j$(nproc) whole
+```
+
 ## Compile bootloader
 ## Compile bootloader
 
 
 ```sh
 ```sh

+ 1 - 1
applications/debug_tools/display_test/display_test.c

@@ -82,7 +82,7 @@ static void display_test_reload_config(DisplayTest* instance) {
         instance->config_contrast,
         instance->config_contrast,
         instance->config_regulation_ratio,
         instance->config_regulation_ratio,
         instance->config_bias);
         instance->config_bias);
-    u8x8_d_st756x_erc_init(
+    u8x8_d_st756x_init(
         &instance->gui->canvas->fb.u8x8,
         &instance->gui->canvas->fb.u8x8,
         instance->config_contrast,
         instance->config_contrast,
         instance->config_regulation_ratio,
         instance->config_regulation_ratio,

+ 1 - 1
applications/desktop/views/desktop_first_start.c

@@ -44,7 +44,7 @@ void desktop_first_start_render(Canvas* canvas, void* model) {
             "%s %s%s",
             "%s %s%s",
             "I am",
             "I am",
             my_name ? my_name : "Unknown",
             my_name ? my_name : "Unknown",
-            ",\ncyberdesktop\nliving in your\npocket >");
+            ",\ncyberdolphin\nliving in your\npocket >");
         canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
         canvas_draw_icon(canvas, 0, height - 48, &I_DolphinFirstStart5_54x49);
         elements_multiline_text_framed(canvas, 60, 17, buf);
         elements_multiline_text_framed(canvas, 60, 17, buf);
     } else if(m->page == 6) {
     } else if(m->page == 6) {

+ 1 - 1
applications/gui/canvas.c

@@ -12,7 +12,7 @@ Canvas* canvas_init() {
     furi_hal_power_insomnia_enter();
     furi_hal_power_insomnia_enter();
 
 
     canvas->orientation = CanvasOrientationHorizontal;
     canvas->orientation = CanvasOrientationHorizontal;
-    u8g2_Setup_st756x_erc(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
+    u8g2_Setup_st756x_flipper(&canvas->fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
 
 
     // send init sequence to the display, display is in sleep mode after this
     // send init sequence to the display, display is in sleep mode after this
     u8g2_InitDisplay(&canvas->fb);
     u8g2_InitDisplay(&canvas->fb);

+ 290 - 0
bootloader/targets/f6/furi-hal/furi-hal-version.c

@@ -0,0 +1,290 @@
+#include <furi-hal-version.h>
+
+#include <stm32wbxx.h>
+#include <stm32wbxx_ll_rtc.h>
+#include <stm32wbxx_ll_system.h>
+
+#include <stdio.h>
+
+#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
+#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
+
+/** OTP V0 Structure: prototypes and early EVT */
+typedef struct {
+    uint8_t board_version;
+    uint8_t board_target;
+    uint8_t board_body;
+    uint8_t board_connect;
+    uint32_t header_timestamp;
+    char name[FURI_HAL_VERSION_NAME_LENGTH];
+} FuriHalVersionOTPv0;
+
+/** OTP V1 Structure: late EVT, DVT */
+typedef struct {
+    /* First 64 bits: header */
+    uint16_t header_magic;
+    uint8_t header_version;
+    uint8_t header_reserved;
+    uint32_t header_timestamp;
+
+    /* Second 64 bits: board info */
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint16_t board_reserved; /** Reserved for future use, 0x0000 */
+
+    /* Third 64 bits: Unique Device Name */
+    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
+} FuriHalVersionOTPv1;
+
+/** OTP V2 Structure: DVT2, PVT, Production */
+typedef struct {
+    /* Early First 64 bits: header */
+    uint16_t header_magic;
+    uint8_t header_version;
+    uint8_t header_reserved;
+    uint32_t header_timestamp;
+
+    /* Early Second 64 bits: board info */
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_display; /** Board display */
+    uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
+    uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
+
+    /* Late Third 64 bits: device info */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
+    uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
+
+    /* Late Fourth 64 bits: Unique Device Name */
+    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
+} FuriHalVersionOTPv2;
+
+/** Represenation Model: */
+typedef struct {
+    uint32_t timestamp;
+
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint8_t board_display; /** Board display */
+
+    char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
+    char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
+    uint8_t ble_mac[6];
+} FuriHalVersion;
+
+static FuriHalVersion furi_hal_version = {0};
+
+static void furi_hal_version_set_name(const char* name) {
+    if(name != NULL) {
+        strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
+        snprintf(
+            furi_hal_version.device_name,
+            FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
+            "xFlipper %s",
+            furi_hal_version.name);
+    } else {
+        snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper");
+    }
+
+    furi_hal_version.device_name[0] = 0;
+
+    // BLE Mac address
+    uint32_t udn = LL_FLASH_GetUDN();
+    uint32_t company_id = LL_FLASH_GetSTCompanyID();
+    uint32_t device_id = LL_FLASH_GetDeviceID();
+    furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
+    furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
+    furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
+    furi_hal_version.ble_mac[3] = (uint8_t)device_id;
+    furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
+    furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
+}
+
+static void furi_hal_version_load_otp_default() {
+    furi_hal_version_set_name(NULL);
+}
+
+static void furi_hal_version_load_otp_v0() {
+    const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    furi_hal_version.timestamp = otp->header_timestamp;
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+
+    furi_hal_version_set_name(otp->name);
+}
+
+static void furi_hal_version_load_otp_v1() {
+    const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    furi_hal_version.timestamp = otp->header_timestamp;
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+    furi_hal_version.board_color = otp->board_color;
+    furi_hal_version.board_region = otp->board_region;
+
+    furi_hal_version_set_name(otp->name);
+}
+
+static void furi_hal_version_load_otp_v2() {
+    const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    // 1st block, programmed afer baking
+    furi_hal_version.timestamp = otp->header_timestamp;
+
+    // 2nd block, programmed afer baking
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+    furi_hal_version.board_display = otp->board_display;
+
+    // 3rd and 4th blocks, programmed on FATP stage
+    if(otp->board_color != 0xFF) {
+        furi_hal_version.board_color = otp->board_color;
+        furi_hal_version.board_region = otp->board_region;
+        furi_hal_version_set_name(otp->name);
+    } else {
+        furi_hal_version.board_color = 0;
+        furi_hal_version.board_region = 0;
+        furi_hal_version_set_name(NULL);
+    }
+}
+
+void furi_hal_version_init() {
+    switch(furi_hal_version_get_otp_version()) {
+    case FuriHalVersionOtpVersionUnknown:
+        furi_hal_version_load_otp_default();
+        break;
+    case FuriHalVersionOtpVersionEmpty:
+        furi_hal_version_load_otp_default();
+        break;
+    case FuriHalVersionOtpVersion0:
+        furi_hal_version_load_otp_v0();
+        break;
+    case FuriHalVersionOtpVersion1:
+        furi_hal_version_load_otp_v1();
+        break;
+    case FuriHalVersionOtpVersion2:
+        furi_hal_version_load_otp_v2();
+        break;
+    default:
+        furi_hal_version_load_otp_default();
+    }
+}
+
+bool furi_hal_version_do_i_belong_here() {
+    return furi_hal_version_get_hw_target() == 7;
+}
+
+const char* furi_hal_version_get_model_name() {
+    return "Flipper Zero";
+}
+
+const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
+    if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
+        return FuriHalVersionOtpVersionEmpty;
+    } else {
+        if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic ==
+           FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
+            // Version 1+
+            uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
+            if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
+                return version;
+            } else {
+                return FuriHalVersionOtpVersionUnknown;
+            }
+        } else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
+            // Version 0
+            return FuriHalVersionOtpVersion0;
+        } else {
+            // Version Unknown
+            return FuriHalVersionOtpVersionUnknown;
+        }
+    }
+}
+
+const uint8_t furi_hal_version_get_hw_version() {
+    return furi_hal_version.board_version;
+}
+
+const uint8_t furi_hal_version_get_hw_target() {
+    return furi_hal_version.board_target;
+}
+
+const uint8_t furi_hal_version_get_hw_body() {
+    return furi_hal_version.board_body;
+}
+
+const FuriHalVersionColor furi_hal_version_get_hw_color() {
+    return furi_hal_version.board_color;
+}
+
+const uint8_t furi_hal_version_get_hw_connect() {
+    return furi_hal_version.board_connect;
+}
+
+const FuriHalVersionRegion furi_hal_version_get_hw_region() {
+    return furi_hal_version.board_region;
+}
+
+const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
+    return furi_hal_version.board_display;
+}
+
+const uint32_t furi_hal_version_get_hw_timestamp() {
+    return furi_hal_version.timestamp;
+}
+
+const char* furi_hal_version_get_name_ptr() {
+    return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
+}
+
+const char* furi_hal_version_get_device_name_ptr() {
+    return furi_hal_version.device_name + 1;
+}
+
+const char* furi_hal_version_get_ble_local_device_name_ptr() {
+    return furi_hal_version.device_name;
+}
+
+const uint8_t* furi_hal_version_get_ble_mac() {
+    return furi_hal_version.ble_mac;
+}
+
+const struct Version* furi_hal_version_get_firmware_version(void) {
+    return version_get();
+}
+
+const struct Version* furi_hal_version_get_boot_version(void) {
+#ifdef NO_BOOTLOADER
+    return 0;
+#else
+    /* Backup register which points to structure in flash memory */
+    return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
+#endif
+}
+
+size_t furi_hal_version_uid_size() {
+    return 64 / 8;
+}
+
+const uint8_t* furi_hal_version_uid() {
+    return (const uint8_t*)UID64_BASE;
+}

+ 1 - 0
bootloader/targets/f6/furi-hal/furi-hal.c

@@ -5,6 +5,7 @@ void furi_hal_init() {
     furi_hal_i2c_init();
     furi_hal_i2c_init();
     furi_hal_light_init();
     furi_hal_light_init();
     furi_hal_spi_init();
     furi_hal_spi_init();
+    furi_hal_version_init();
 }
 }
 
 
 void delay(float milliseconds) {
 void delay(float milliseconds) {

+ 1 - 1
bootloader/targets/f6/target.c

@@ -189,7 +189,7 @@ void target_display_init() {
     hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
     hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
     // Initialize
     // Initialize
     u8g2_t fb;
     u8g2_t fb;
-    u8g2_Setup_st756x_erc(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
+    u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
     u8g2_InitDisplay(&fb);
     u8g2_InitDisplay(&fb);
     // Create payload
     // Create payload
     u8g2_ClearBuffer(&fb);
     u8g2_ClearBuffer(&fb);

+ 290 - 0
bootloader/targets/f7/furi-hal/furi-hal-version.c

@@ -0,0 +1,290 @@
+#include <furi-hal-version.h>
+
+#include <stm32wbxx.h>
+#include <stm32wbxx_ll_rtc.h>
+#include <stm32wbxx_ll_system.h>
+
+#include <stdio.h>
+
+#define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
+#define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
+
+/** OTP V0 Structure: prototypes and early EVT */
+typedef struct {
+    uint8_t board_version;
+    uint8_t board_target;
+    uint8_t board_body;
+    uint8_t board_connect;
+    uint32_t header_timestamp;
+    char name[FURI_HAL_VERSION_NAME_LENGTH];
+} FuriHalVersionOTPv0;
+
+/** OTP V1 Structure: late EVT, DVT */
+typedef struct {
+    /* First 64 bits: header */
+    uint16_t header_magic;
+    uint8_t header_version;
+    uint8_t header_reserved;
+    uint32_t header_timestamp;
+
+    /* Second 64 bits: board info */
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint16_t board_reserved; /** Reserved for future use, 0x0000 */
+
+    /* Third 64 bits: Unique Device Name */
+    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
+} FuriHalVersionOTPv1;
+
+/** OTP V2 Structure: DVT2, PVT, Production */
+typedef struct {
+    /* Early First 64 bits: header */
+    uint16_t header_magic;
+    uint8_t header_version;
+    uint8_t header_reserved;
+    uint32_t header_timestamp;
+
+    /* Early Second 64 bits: board info */
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_display; /** Board display */
+    uint8_t board_reserved2_0; /** Reserved for future use, 0x00 */
+    uint16_t board_reserved2_1; /** Reserved for future use, 0x0000 */
+
+    /* Late Third 64 bits: device info */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint16_t board_reserved3_0; /** Reserved for future use, 0x0000 */
+    uint32_t board_reserved3_1; /** Reserved for future use, 0x00000000 */
+
+    /* Late Fourth 64 bits: Unique Device Name */
+    char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
+} FuriHalVersionOTPv2;
+
+/** Represenation Model: */
+typedef struct {
+    uint32_t timestamp;
+
+    uint8_t board_version; /** Board version */
+    uint8_t board_target; /** Board target firmware */
+    uint8_t board_body; /** Board body */
+    uint8_t board_connect; /** Board interconnect */
+    uint8_t board_color; /** Board color */
+    uint8_t board_region; /** Board region */
+    uint8_t board_display; /** Board display */
+
+    char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
+    char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH]; /** device name for special needs */
+    uint8_t ble_mac[6];
+} FuriHalVersion;
+
+static FuriHalVersion furi_hal_version = {0};
+
+static void furi_hal_version_set_name(const char* name) {
+    if(name != NULL) {
+        strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
+        snprintf(
+            furi_hal_version.device_name,
+            FURI_HAL_VERSION_DEVICE_NAME_LENGTH,
+            "xFlipper %s",
+            furi_hal_version.name);
+    } else {
+        snprintf(furi_hal_version.device_name, FURI_HAL_VERSION_DEVICE_NAME_LENGTH, "xFlipper");
+    }
+
+    furi_hal_version.device_name[0] = 0;
+
+    // BLE Mac address
+    uint32_t udn = LL_FLASH_GetUDN();
+    uint32_t company_id = LL_FLASH_GetSTCompanyID();
+    uint32_t device_id = LL_FLASH_GetDeviceID();
+    furi_hal_version.ble_mac[0] = (uint8_t)(udn & 0x000000FF);
+    furi_hal_version.ble_mac[1] = (uint8_t)((udn & 0x0000FF00) >> 8);
+    furi_hal_version.ble_mac[2] = (uint8_t)((udn & 0x00FF0000) >> 16);
+    furi_hal_version.ble_mac[3] = (uint8_t)device_id;
+    furi_hal_version.ble_mac[4] = (uint8_t)(company_id & 0x000000FF);
+    furi_hal_version.ble_mac[5] = (uint8_t)((company_id & 0x0000FF00) >> 8);
+}
+
+static void furi_hal_version_load_otp_default() {
+    furi_hal_version_set_name(NULL);
+}
+
+static void furi_hal_version_load_otp_v0() {
+    const FuriHalVersionOTPv0* otp = (FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    furi_hal_version.timestamp = otp->header_timestamp;
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+
+    furi_hal_version_set_name(otp->name);
+}
+
+static void furi_hal_version_load_otp_v1() {
+    const FuriHalVersionOTPv1* otp = (FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    furi_hal_version.timestamp = otp->header_timestamp;
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+    furi_hal_version.board_color = otp->board_color;
+    furi_hal_version.board_region = otp->board_region;
+
+    furi_hal_version_set_name(otp->name);
+}
+
+static void furi_hal_version_load_otp_v2() {
+    const FuriHalVersionOTPv2* otp = (FuriHalVersionOTPv2*)FURI_HAL_VERSION_OTP_ADDRESS;
+
+    // 1st block, programmed afer baking
+    furi_hal_version.timestamp = otp->header_timestamp;
+
+    // 2nd block, programmed afer baking
+    furi_hal_version.board_version = otp->board_version;
+    furi_hal_version.board_target = otp->board_target;
+    furi_hal_version.board_body = otp->board_body;
+    furi_hal_version.board_connect = otp->board_connect;
+    furi_hal_version.board_display = otp->board_display;
+
+    // 3rd and 4th blocks, programmed on FATP stage
+    if(otp->board_color != 0xFF) {
+        furi_hal_version.board_color = otp->board_color;
+        furi_hal_version.board_region = otp->board_region;
+        furi_hal_version_set_name(otp->name);
+    } else {
+        furi_hal_version.board_color = 0;
+        furi_hal_version.board_region = 0;
+        furi_hal_version_set_name(NULL);
+    }
+}
+
+void furi_hal_version_init() {
+    switch(furi_hal_version_get_otp_version()) {
+    case FuriHalVersionOtpVersionUnknown:
+        furi_hal_version_load_otp_default();
+        break;
+    case FuriHalVersionOtpVersionEmpty:
+        furi_hal_version_load_otp_default();
+        break;
+    case FuriHalVersionOtpVersion0:
+        furi_hal_version_load_otp_v0();
+        break;
+    case FuriHalVersionOtpVersion1:
+        furi_hal_version_load_otp_v1();
+        break;
+    case FuriHalVersionOtpVersion2:
+        furi_hal_version_load_otp_v2();
+        break;
+    default:
+        furi_hal_version_load_otp_default();
+    }
+}
+
+bool furi_hal_version_do_i_belong_here() {
+    return furi_hal_version_get_hw_target() == 7;
+}
+
+const char* furi_hal_version_get_model_name() {
+    return "Flipper Zero";
+}
+
+const FuriHalVersionOtpVersion furi_hal_version_get_otp_version() {
+    if(*(uint64_t*)FURI_HAL_VERSION_OTP_ADDRESS == 0xFFFFFFFF) {
+        return FuriHalVersionOtpVersionEmpty;
+    } else {
+        if(((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_magic ==
+           FURI_HAL_VERSION_OTP_HEADER_MAGIC) {
+            // Version 1+
+            uint8_t version = ((FuriHalVersionOTPv1*)FURI_HAL_VERSION_OTP_ADDRESS)->header_version;
+            if(version >= FuriHalVersionOtpVersion1 && version <= FuriHalVersionOtpVersion2) {
+                return version;
+            } else {
+                return FuriHalVersionOtpVersionUnknown;
+            }
+        } else if(((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
+            // Version 0
+            return FuriHalVersionOtpVersion0;
+        } else {
+            // Version Unknown
+            return FuriHalVersionOtpVersionUnknown;
+        }
+    }
+}
+
+const uint8_t furi_hal_version_get_hw_version() {
+    return furi_hal_version.board_version;
+}
+
+const uint8_t furi_hal_version_get_hw_target() {
+    return furi_hal_version.board_target;
+}
+
+const uint8_t furi_hal_version_get_hw_body() {
+    return furi_hal_version.board_body;
+}
+
+const FuriHalVersionColor furi_hal_version_get_hw_color() {
+    return furi_hal_version.board_color;
+}
+
+const uint8_t furi_hal_version_get_hw_connect() {
+    return furi_hal_version.board_connect;
+}
+
+const FuriHalVersionRegion furi_hal_version_get_hw_region() {
+    return furi_hal_version.board_region;
+}
+
+const FuriHalVersionDisplay furi_hal_version_get_hw_display() {
+    return furi_hal_version.board_display;
+}
+
+const uint32_t furi_hal_version_get_hw_timestamp() {
+    return furi_hal_version.timestamp;
+}
+
+const char* furi_hal_version_get_name_ptr() {
+    return *furi_hal_version.name == 0x00 ? NULL : furi_hal_version.name;
+}
+
+const char* furi_hal_version_get_device_name_ptr() {
+    return furi_hal_version.device_name + 1;
+}
+
+const char* furi_hal_version_get_ble_local_device_name_ptr() {
+    return furi_hal_version.device_name;
+}
+
+const uint8_t* furi_hal_version_get_ble_mac() {
+    return furi_hal_version.ble_mac;
+}
+
+const struct Version* furi_hal_version_get_firmware_version(void) {
+    return version_get();
+}
+
+const struct Version* furi_hal_version_get_boot_version(void) {
+#ifdef NO_BOOTLOADER
+    return 0;
+#else
+    /* Backup register which points to structure in flash memory */
+    return (const struct Version*)LL_RTC_BAK_GetRegister(RTC, LL_RTC_BKP_DR1);
+#endif
+}
+
+size_t furi_hal_version_uid_size() {
+    return 64 / 8;
+}
+
+const uint8_t* furi_hal_version_uid() {
+    return (const uint8_t*)UID64_BASE;
+}

+ 1 - 0
bootloader/targets/f7/furi-hal/furi-hal.c

@@ -5,6 +5,7 @@ void furi_hal_init() {
     furi_hal_i2c_init();
     furi_hal_i2c_init();
     furi_hal_light_init();
     furi_hal_light_init();
     furi_hal_spi_init();
     furi_hal_spi_init();
+    furi_hal_version_init();
 }
 }
 
 
 void delay(float milliseconds) {
 void delay(float milliseconds) {

+ 0 - 14
bootloader/targets/f7/furi-hal/furi-hal.h

@@ -1,14 +0,0 @@
-#pragma once
-
-#include <furi-hal-i2c.h>
-#include <furi-hal-light.h>
-#include <furi-hal-resources.h>
-#include <furi-hal-spi.h>
-
-#define furi_assert(value) (void)(value)
-
-void furi_hal_init();
-
-void delay(float milliseconds);
-
-void delay_us(float microseconds);

+ 1 - 1
bootloader/targets/f7/target.c

@@ -189,7 +189,7 @@ void target_display_init() {
     hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
     hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
     // Initialize
     // Initialize
     u8g2_t fb;
     u8g2_t fb;
-    u8g2_Setup_st756x_erc(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
+    u8g2_Setup_st756x_flipper(&fb, U8G2_R0, u8x8_hw_spi_stm32, u8g2_gpio_and_delay_stm32);
     u8g2_InitDisplay(&fb);
     u8g2_InitDisplay(&fb);
     // Create payload
     // Create payload
     u8g2_ClearBuffer(&fb);
     u8g2_ClearBuffer(&fb);

+ 173 - 0
bootloader/targets/furi-hal-include/furi-hal-version.h

@@ -0,0 +1,173 @@
+/**
+ * @file furi-hal-version.h
+ * Version HAL API
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <string.h>
+#include <lib/toolbox/version.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define FURI_HAL_VERSION_NAME_LENGTH 8
+#define FURI_HAL_VERSION_ARRAY_NAME_LENGTH (FURI_HAL_VERSION_NAME_LENGTH + 1)
+/** BLE symbol + "Flipper " + name */
+#define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
+
+/** OTP Versions enum */
+typedef enum {
+    FuriHalVersionOtpVersion0 = 0x00,
+    FuriHalVersionOtpVersion1 = 0x01,
+    FuriHalVersionOtpVersion2 = 0x02,
+    FuriHalVersionOtpVersionEmpty = 0xFFFFFFFE,
+    FuriHalVersionOtpVersionUnknown = 0xFFFFFFFF,
+} FuriHalVersionOtpVersion;
+
+/** Device Colors */
+typedef enum {
+    FuriHalVersionColorUnknown = 0x00,
+    FuriHalVersionColorBlack = 0x01,
+    FuriHalVersionColorWhite = 0x02,
+} FuriHalVersionColor;
+
+/** Device Regions */
+typedef enum {
+    FuriHalVersionRegionUnknown = 0x00,
+    FuriHalVersionRegionEuRu = 0x01,
+    FuriHalVersionRegionUsCaAu = 0x02,
+    FuriHalVersionRegionJp = 0x03,
+} FuriHalVersionRegion;
+
+/** Device Display */
+typedef enum {
+    FuriHalVersionDisplayUnknown = 0x00,
+    FuriHalVersionDisplayErc = 0x01,
+    FuriHalVersionDisplayMgg = 0x02,
+} FuriHalVersionDisplay;
+
+/** Init flipper version
+ */
+void furi_hal_version_init();
+
+/** Check target firmware version
+ *
+ * @return     true if target and real matches
+ */
+bool furi_hal_version_do_i_belong_here();
+
+/** Get model name
+ *
+ * @return     model name C-string
+ */
+const char* furi_hal_version_get_model_name();
+
+/** Get OTP version
+ *
+ * @return     OTP Version
+ */
+const FuriHalVersionOtpVersion furi_hal_version_get_otp_version();
+
+/** Get hardware version
+ *
+ * @return     Hardware Version
+ */
+const uint8_t furi_hal_version_get_hw_version();
+
+/** Get hardware target
+ *
+ * @return     Hardware Target
+ */
+const uint8_t furi_hal_version_get_hw_target();
+
+/** Get hardware body
+ *
+ * @return     Hardware Body
+ */
+const uint8_t furi_hal_version_get_hw_body();
+
+/** Get hardware body color
+ *
+ * @return     Hardware Color
+ */
+const FuriHalVersionColor furi_hal_version_get_hw_color();
+
+/** Get hardware connect
+ *
+ * @return     Hardware Interconnect
+ */
+const uint8_t furi_hal_version_get_hw_connect();
+
+/** Get hardware region
+ *
+ * @return     Hardware Region
+ */
+const FuriHalVersionRegion furi_hal_version_get_hw_region();
+
+/** Get hardware display id
+ *
+ * @return     Display id
+ */
+const FuriHalVersionDisplay furi_hal_version_get_hw_display();
+
+/** Get hardware timestamp
+ *
+ * @return     Hardware Manufacture timestamp
+ */
+const uint32_t furi_hal_version_get_hw_timestamp();
+
+/** Get pointer to target name
+ *
+ * @return     Hardware Name C-string
+ */
+const char* furi_hal_version_get_name_ptr();
+
+/** Get pointer to target device name
+ *
+ * @return     Hardware Device Name C-string
+ */
+const char* furi_hal_version_get_device_name_ptr();
+
+/** Get pointer to target ble local device name
+ *
+ * @return     Ble Device Name C-string
+ */
+const char* furi_hal_version_get_ble_local_device_name_ptr();
+
+/** Get BLE MAC address
+ *
+ * @return     pointer to BLE MAC address
+ */
+const uint8_t* furi_hal_version_get_ble_mac();
+
+/** Get address of version structure of bootloader, stored in chip flash.
+ *
+ * @return     Address of boot version structure.
+ */
+const struct Version* furi_hal_version_get_boot_version();
+
+/** Get address of version structure of firmware.
+ *
+ * @return     Address of firmware version structure.
+ */
+const struct Version* furi_hal_version_get_firmware_version();
+
+/** Get platform UID size in bytes
+ *
+ * @return     UID size in bytes
+ */
+size_t furi_hal_version_uid_size();
+
+/** Get const pointer to UID
+ *
+ * @return     pointer to UID
+ */
+const uint8_t* furi_hal_version_uid();
+
+#ifdef __cplusplus
+}
+#endif

+ 1 - 0
bootloader/targets/f6/furi-hal/furi-hal.h → bootloader/targets/furi-hal-include/furi-hal.h

@@ -4,6 +4,7 @@
 #include <furi-hal-light.h>
 #include <furi-hal-light.h>
 #include <furi-hal-resources.h>
 #include <furi-hal-resources.h>
 #include <furi-hal-spi.h>
 #include <furi-hal-spi.h>
+#include <furi-hal-version.h>
 
 
 #define furi_assert(value) (void)(value)
 #define furi_assert(value) (void)(value)
 
 

+ 22 - 11
lib/u8g2/u8g2_glue.c

@@ -185,7 +185,7 @@ uint8_t u8x8_d_st756x_common(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *a
     return 1;
     return 1;
 }
 }
 
 
-void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias) {
+void u8x8_d_st756x_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias) {
     contrast = contrast & 0b00111111;
     contrast = contrast & 0b00111111;
     regulation_ratio = regulation_ratio & 0b111;
     regulation_ratio = regulation_ratio & 0b111;
 
 
@@ -209,7 +209,7 @@ void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_r
     u8x8_cad_EndTransfer(u8x8);
     u8x8_cad_EndTransfer(u8x8);
 }
 }
 
 
-uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
+uint8_t u8x8_d_st756x_flipper(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
     /* call common procedure first and handle messages there */
     /* call common procedure first and handle messages there */
     if (u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) {
     if (u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) {
         /* msg not handled, then try here */
         /* msg not handled, then try here */
@@ -219,13 +219,24 @@ uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_
             break;
             break;
         case U8X8_MSG_DISPLAY_INIT:
         case U8X8_MSG_DISPLAY_INIT:
             u8x8_d_helper_display_init(u8x8);
             u8x8_d_helper_display_init(u8x8);
-            /* Bias, EV and Regulation Ration
-             * EV = 32
-             * RR = V0 / ((1 - (63 - EV) / 162) * 2.1)
-             * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 5.5 (0b101)
-             * Bias = 1/9 (false)
-             */
-            u8x8_d_st756x_erc_init(u8x8, 32, 0b101, false);
+            FuriHalVersionDisplay display = furi_hal_version_get_hw_display();
+            if (display == FuriHalVersionDisplayMgg) {
+                /* MGG v0+(ST7567)
+                 * EV = 32
+                 * RR = V0 / ((1 - (63 - EV) / 162) * 2.1)
+                 * RR = 10 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.88 is 6 (0b110)
+                 * Bias = 1/9 (false)
+                 */
+                u8x8_d_st756x_init(u8x8, 32, 0b110, false);
+            } else {
+                /* ERC v1(ST7565) and v2(ST7567)
+                 * EV = 33
+                 * RR = V0 / ((1 - (63 - EV) / 162) * 2.1)
+                 * RR = 9.3 / ((1 - (63 - 32) / 162) * 2.1) ~= 5.47 is 5.5 (0b101)
+                 * Bias = 1/9 (false)
+                 */
+                u8x8_d_st756x_init(u8x8, 33, 0b101, false);
+            }
             break;
             break;
         case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
         case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
             if ( arg_int == 0 ) {
             if ( arg_int == 0 ) {
@@ -244,10 +255,10 @@ uint8_t u8x8_d_st756x_erc(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_
     return 1;
     return 1;
 }
 }
 
 
-void u8g2_Setup_st756x_erc(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) {
+void u8g2_Setup_st756x_flipper(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb) {
     uint8_t tile_buf_height;
     uint8_t tile_buf_height;
     uint8_t *buf;
     uint8_t *buf;
-    u8g2_SetupDisplay(u8g2, u8x8_d_st756x_erc, u8x8_cad_001, byte_cb, gpio_and_delay_cb);
+    u8g2_SetupDisplay(u8g2, u8x8_d_st756x_flipper, u8x8_cad_001, byte_cb, gpio_and_delay_cb);
     buf = u8g2_m_16_8_f(&tile_buf_height);
     buf = u8g2_m_16_8_f(&tile_buf_height);
     u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
     u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
 }
 }

+ 2 - 2
lib/u8g2/u8g2_glue.h

@@ -7,6 +7,6 @@ uint8_t u8g2_gpio_and_delay_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, vo
 
 
 uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
 uint8_t u8x8_hw_spi_stm32(u8x8_t* u8x8, uint8_t msg, uint8_t arg_int, void* arg_ptr);
 
 
-void u8g2_Setup_st756x_erc(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb);
+void u8g2_Setup_st756x_flipper(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb);
 
 
-void u8x8_d_st756x_erc_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias);
+void u8x8_d_st756x_init(u8x8_t *u8x8, uint8_t contrast, uint8_t regulation_ratio, bool bias);

+ 2 - 2
make/rules.mk

@@ -140,7 +140,7 @@ generate_cscope_db:
 	@cscope -b -k -i $(OBJ_DIR)/source.list -f $(OBJ_DIR)/cscope.out
 	@cscope -b -k -i $(OBJ_DIR)/source.list -f $(OBJ_DIR)/cscope.out
 	@rm -rf $(OBJ_DIR)/source.list $(OBJ_DIR)/source.list.p
 	@rm -rf $(OBJ_DIR)/source.list $(OBJ_DIR)/source.list.p
 
 
+# Prevent make from trying to find .d targets
+%.d: ;
 
 
-ifneq ("$(wildcard $(OBJ_DIR)/*.d)","")
 -include $(DEPS)
 -include $(DEPS)
-endif

+ 9 - 12
scripts/ReadMe.md

@@ -1,6 +1,6 @@
 # About
 # About
 
 
-This folder contains differnt scripts that automates routine actions.
+This folder contains supplementary scripts that automates routine actions.
 Flashing scripts are based on cli version of [STM32CubeProgrammer](https://www.st.com/en/development-tools/stm32cubeprog.html).
 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.
 You will need to add STM32_Programmer_CLI to your path to use them.
 
 
@@ -9,29 +9,26 @@ You will need to add STM32_Programmer_CLI to your path to use them.
 Always flash your device in the folllowing sequence:
 Always flash your device in the folllowing sequence:
 
 
 - OTP (Only on empty MCU)
 - OTP (Only on empty MCU)
-- Core2 firmware
-- Core1 firmware
+- Core1 and Core2 firmware flashing
 - Option Bytes
 - Option Bytes
 
 
 ## Otp flashing
 ## Otp flashing
 
 
 !!! Flashing incorrect OTP may permanently brick your device !!!
 !!! Flashing incorrect OTP may permanently brick your device !!!
 
 
-Normally OTP data generated and flashed at factory.
+Normally OTP data generated and flashed at the factory.
 In case if MCU was replaced you'll need correct OTP data to be able to use companion applications.
 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.
+Use `otp.py` to generate and flash OTP data.
 You will need exact main board revision to genrate OTP data. It can be found on main PCB.
 You will need exact main board revision to genrate OTP data. It can be found on main PCB.
+Also display type, region and etc...
 
 
 !!! Flashing incorrect OTP may permanently brick your device !!!
 !!! Flashing incorrect OTP may permanently brick your device !!!
 
 
-## Core2 flashing
+## Core1 and Core2 firmware 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.
+Main flashing sequence can be found in root `Makefile`.
+Core2 goes first, then Core1.
+Never flash FUS or you will loose your job, girlfriend and keys in secure enclave.
 
 
 ## Option Bytes
 ## Option Bytes
 
 

+ 152 - 0
scripts/flash.py

@@ -0,0 +1,152 @@
+#!/usr/bin/env python3
+
+import logging
+import argparse
+import sys
+import os
+
+from flipper.app import App
+from flipper.cube import CubeProgrammer
+
+STATEMENT = "AGREE_TO_LOOSE_FLIPPER_FEATURES_THAT_USES_CRYPTO_ENCLAVE"
+
+
+class Main(App):
+    def init(self):
+        self.subparsers = self.parser.add_subparsers(help="sub-command help")
+        # Wipe
+        self.parser_wipe = self.subparsers.add_parser("wipe", help="Wipe MCU Flash")
+        self.parser_wipe.set_defaults(func=self.wipe)
+        # Core 1 boot
+        self.parser_core1boot = self.subparsers.add_parser(
+            "core1boot", help="Flash Core1 Bootloader"
+        )
+        self._addArgsSWD(self.parser_core1boot)
+        self.parser_core1boot.add_argument(
+            "bootloader", type=str, help="Bootloader binary"
+        )
+        self.parser_core1boot.set_defaults(func=self.core1boot)
+        # Core 1 firmware
+        self.parser_core1firmware = self.subparsers.add_parser(
+            "core1firmware", help="Flash Core1 Firmware"
+        )
+        self._addArgsSWD(self.parser_core1firmware)
+        self.parser_core1firmware.add_argument(
+            "firmware", type=str, help="Firmware binary"
+        )
+        self.parser_core1firmware.set_defaults(func=self.core1firmware)
+        # Core 1 all
+        self.parser_core1 = self.subparsers.add_parser(
+            "core1", help="Flash Core1 Boot and Firmware"
+        )
+        self._addArgsSWD(self.parser_core1)
+        self.parser_core1.add_argument("bootloader", type=str, help="Bootloader binary")
+        self.parser_core1.add_argument("firmware", type=str, help="Firmware binary")
+        self.parser_core1.set_defaults(func=self.core1)
+        # Core 2 fus
+        self.parser_core2fus = self.subparsers.add_parser(
+            "core2fus", help="Flash Core2 Firmware Update Service"
+        )
+        self._addArgsSWD(self.parser_core2fus)
+        self.parser_core2fus.add_argument(
+            "--statement",
+            type=str,
+            help="NEVER FLASH FUS, IT WILL ERASE CRYPTO ENCLAVE",
+            required=True,
+        )
+        self.parser_core2fus.add_argument(
+            "fus_address", type=str, help="Firmware Update Service Address"
+        )
+        self.parser_core2fus.add_argument(
+            "fus", type=str, help="Firmware Update Service Binary"
+        )
+        self.parser_core2fus.set_defaults(func=self.core2fus)
+        # Core 2 radio stack
+        self.parser_core2radio = self.subparsers.add_parser(
+            "core2radio", help="Flash Core2 Radio stack"
+        )
+        self._addArgsSWD(self.parser_core2radio)
+        self.parser_core2radio.add_argument(
+            "radio_address", type=str, help="Radio Stack Binary Address"
+        )
+        self.parser_core2radio.add_argument(
+            "radio", type=str, help="Radio Stack Binary"
+        )
+        self.parser_core2radio.set_defaults(func=self.core2radio)
+
+    def _addArgsSWD(self, parser):
+        parser.add_argument(
+            "--port", type=str, help="Port to connect: swd or usb1", default="swd"
+        )
+
+    def wipe(self):
+        self.logger.info(f"Wiping flash")
+        cp = CubeProgrammer("swd")
+        self.logger.info(f"Setting RDP to 0xBB")
+        cp.setOptionBytes({"RDP": ("0xBB", "rw")})
+        self.logger.info(f"Verifying RDP")
+        r = cp.checkOptionBytes({"RDP": ("0xBB", "rw")})
+        assert r == True
+        self.logger.info(f"Result: {r}")
+        self.logger.info(f"Setting RDP to 0xAA")
+        cp.setOptionBytes({"RDP": ("0xAA", "rw")})
+        self.logger.info(f"Verifying RDP")
+        r = cp.checkOptionBytes({"RDP": ("0xAA", "rw")})
+        assert r == True
+        self.logger.info(f"Result: {r}")
+        self.logger.info(f"Complete")
+        return 0
+
+    def core1boot(self):
+        self.logger.info(f"Flashing bootloader")
+        cp = CubeProgrammer(self.args.port)
+        cp.flashBin("0x08000000", self.args.bootloader)
+        self.logger.info(f"Complete")
+        cp.resetTarget()
+        return 0
+
+    def core1firmware(self):
+        self.logger.info(f"Flashing firmware")
+        cp = CubeProgrammer(self.args.port)
+        cp.flashBin("0x08008000", self.args.firmware)
+        self.logger.info(f"Complete")
+        cp.resetTarget()
+        return 0
+
+    def core1(self):
+        self.logger.info(f"Flashing bootloader")
+        cp = CubeProgrammer(self.args.port)
+        cp.flashBin("0x08000000", self.args.bootloader)
+        self.logger.info(f"Flashing firmware")
+        cp.flashBin("0x08008000", self.args.firmware)
+        cp.resetTarget()
+        self.logger.info(f"Complete")
+        return 0
+
+    def core2fus(self):
+        if self.args.statement != STATEMENT:
+            self.logger.error(
+                f"PLEASE DON'T. THIS FEATURE INTENDED ONLY FOR FACTORY FLASHING"
+            )
+            return 1
+        self.logger.info(f"Flashing Firmware Update Service")
+        cp = CubeProgrammer(self.args.port)
+        cp.flashCore2(self.args.fus_address, self.args.fus)
+        self.logger.info(f"Complete")
+        return 0
+
+    def core2radio(self):
+        if int(self.args.radio_address, 16) > 0x080E0000:
+            self.logger.error(f"I KNOW WHAT YOU DID LAST SUMMER")
+            return 1
+        cp = CubeProgrammer(self.args.port)
+        self.logger.info(f"Removing Current Radio Stack")
+        cp.deleteCore2RadioStack()
+        self.logger.info(f"Flashing Radio Stack")
+        cp.flashCore2(self.args.radio_address, self.args.radio)
+        self.logger.info(f"Complete")
+        return 0
+
+
+if __name__ == "__main__":
+    Main()()

+ 0 - 12
scripts/flash_core1_main_swd.sh

@@ -1,12 +0,0 @@
-#!/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

+ 0 - 20
scripts/flash_core2_ble_swd.sh

@@ -1,20 +0,0 @@
-#!/bin/bash
-
-set -x -e
-
-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_for_fus_0_5_3.bin 0x080EC000 || true
-STM32_Programmer_CLI -c port=swd
-
-STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_FUS_fw.bin 0x080EC000 || true
-STM32_Programmer_CLI -c port=swd
-
-STM32_Programmer_CLI -c port=swd -fwdelete
-
-STM32_Programmer_CLI -c port=swd -fwupgrade "$COPRO_DIR"/stm32wb5x_BLE_Stack_full_fw.bin 0x080CA000 firstinstall=0
-
-STM32_Programmer_CLI -c port=swd -ob nSWBOOT0=1 nBOOT0=1
-

+ 0 - 17
scripts/flash_otp_version_dfu.sh

@@ -1,17 +0,0 @@
-#!/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 - 17
scripts/flash_otp_version_swd.sh

@@ -1,17 +0,0 @@
-#!/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=swd -d "$1" 0x1FFF7000
-
-STM32_Programmer_CLI -c port=swd -r8 0x1FFF7000 8

+ 0 - 11
scripts/flash_wipe_swd.sh

@@ -1,11 +0,0 @@
-#!/bin/bash
-
-set -x -e
-
-STM32_Programmer_CLI -c port=swd -ob RDP=0xBB
-
-STM32_Programmer_CLI -c port=swd -ob displ
-
-STM32_Programmer_CLI -c port=swd --readunprotect
-
-STM32_Programmer_CLI -c port=swd -ob displ

+ 47 - 0
scripts/flipper/app.py

@@ -0,0 +1,47 @@
+import logging
+import argparse
+import sys
+import os
+
+
+class App:
+    def __init__(self):
+        # Argument Parser
+        self.parser = argparse.ArgumentParser()
+        self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
+        # Logging
+        self.logger = logging.getLogger()
+        # Application specific initialization
+        self.init()
+
+    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.before()
+        return_code = self.args.func()
+        self.after()
+        if isinstance(return_code, int):
+            exit(return_code)
+        else:
+            self.logger.error(f"Missing return code")
+            exit(255)
+
+    def init(self):
+        raise Exception("init() is not implemented")
+
+    def before(self):
+        pass
+
+    def after(self):
+        pass

+ 91 - 0
scripts/flipper/cube.py

@@ -0,0 +1,91 @@
+import logging
+import subprocess
+
+
+class CubeProgrammer:
+    """STM32 Cube Programmer cli wrapper"""
+
+    def __init__(self, port, params=[]):
+        self.port = port
+        self.params = params
+        # logging
+        self.logger = logging.getLogger()
+
+    def _execute(self, args):
+        try:
+            output = subprocess.check_output(
+                [
+                    "STM32_Programmer_CLI",
+                    "-q",
+                    f"-c port={self.port}",
+                    *self.params,
+                    *args,
+                ]
+            )
+        except subprocess.CalledProcessError as e:
+            if e.output:
+                print("Process output:\n", e.output.decode())
+            print("Process return code:", e.returncode)
+            raise e
+        assert output
+        return output.decode()
+
+    def getVersion(self):
+        output = self._execute(["--version"])
+
+    def checkOptionBytes(self, option_bytes):
+        output = self._execute(["-ob displ"])
+        ob_correct = True
+        for line in output.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 option_bytes.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 option_bytes[key][0] != value:
+                self.logger.error(
+                    f"Invalid OB: {key} {value}, expected: {option_bytes[key][0]}"
+                )
+                ob_correct = False
+        return ob_correct
+
+    def setOptionBytes(self, option_bytes):
+        options = []
+        for key, (value, attr) in option_bytes.items():
+            if "w" in attr:
+                options.append(f"{key}={value}")
+        self._execute(["-ob", *options])
+        return True
+
+    def flashBin(self, address, filename):
+        self._execute(
+            [
+                "-d",
+                filename,
+                f"{address}",
+            ]
+        )
+
+    def flashCore2(self, address, filename):
+        self._execute(
+            [
+                "-fwupgrade",
+                filename,
+                f"{address}",
+            ]
+        )
+
+    def deleteCore2RadioStack(self):
+        self._execute(["-fwdelete"])
+
+    def resetTarget(self):
+        self._execute([])

+ 1 - 1
scripts/ob.data

@@ -25,7 +25,7 @@ SBRSA:0xA:r
 SBRV:0x32800:r
 SBRV:0x32800:r
 PCROP1A_STRT:0x1FF:r
 PCROP1A_STRT:0x1FF:r
 PCROP1A_END:0x0:r
 PCROP1A_END:0x0:r
-PCROP_RDP:0x1:r
+PCROP_RDP:0x1:rw
 PCROP1B_STRT:0x1FF:r
 PCROP1B_STRT:0x1FF:r
 PCROP1B_END:0x0:r
 PCROP1B_END:0x0:r
 WRP1A_STRT:0xFF:r
 WRP1A_STRT:0xFF:r

+ 17 - 99
scripts/ob.py

@@ -6,12 +6,12 @@ import subprocess
 import sys
 import sys
 import os
 import os
 
 
+from flipper.app import App
+from flipper.cube import CubeProgrammer
 
 
-class Main:
-    def __init__(self):
-        # command args
-        self.parser = argparse.ArgumentParser()
-        self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
+
+class Main(App):
+    def init(self):
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
         self.parser_check = self.subparsers.add_parser(
         self.parser_check = self.subparsers.add_parser(
             "check", help="Check Option Bytes"
             "check", help="Check Option Bytes"
@@ -20,39 +20,16 @@ class Main:
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
         )
         )
         self.parser_check.set_defaults(func=self.check)
         self.parser_check.set_defaults(func=self.check)
+        # Set command
         self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
         self.parser_set = self.subparsers.add_parser("set", help="Set Option Bytes")
         self.parser_set.add_argument(
         self.parser_set.add_argument(
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
         )
         )
         self.parser_set.set_defaults(func=self.set)
         self.parser_set.set_defaults(func=self.set)
-        # logging
-        self.logger = logging.getLogger()
         # OB
         # OB
         self.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()
-
-        return_code = self.args.func()
-        if isinstance(return_code, int):
-            return return_code
-        else:
-            self.logger.error(f"Forgotten return code")
-            return 255
-
-    def loadOB(self):
+    def before(self):
         self.logger.info(f"Loading Option Bytes data")
         self.logger.info(f"Loading Option Bytes data")
         file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
         file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
         file = open(file_path, "r")
         file = open(file_path, "r")
@@ -62,47 +39,8 @@ class Main:
 
 
     def check(self):
     def check(self):
         self.logger.info(f"Checking Option Bytes")
         self.logger.info(f"Checking Option Bytes")
-        try:
-            output = subprocess.check_output(
-                [
-                    "STM32_Programmer_CLI",
-                    "-q",
-                    "-c",
-                    f"port={self.args.port}",
-                    "-ob displ",
-                ]
-            )
-            assert output
-        except subprocess.CalledProcessError as e:
-            self.logger.error(e.output.decode())
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
-            return 127
-        except Exception as e:
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
-            self.logger.exception(e)
-            return 126
-        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:
+        cp = CubeProgrammer(self.args.port)
+        if cp.checkOptionBytes(self.ob):
             self.logger.info(f"OB Check OK")
             self.logger.info(f"OB Check OK")
             return 0
             return 0
         else:
         else:
@@ -111,34 +49,14 @@ class Main:
 
 
     def set(self):
     def set(self):
         self.logger.info(f"Setting Option Bytes")
         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",
-                    f"port={self.args.port}",
-                    "-ob",
-                    *options,
-                ]
-            )
-            assert output
-            self.logger.info(f"Success")
-        except subprocess.CalledProcessError as e:
-            self.logger.error(e.output.decode())
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
-            return 125
-        except Exception as e:
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
-            self.logger.exception(e)
-            return 124
-        return 0
+        cp = CubeProgrammer(self.args.port)
+        if cp.setOptionBytes(self.ob):
+            self.logger.info(f"OB Set OK")
+            return 0
+        else:
+            self.logger.error(f"OB Set FAIL")
+            return 255
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":
-    return_code = Main()()
-    exit(return_code)
+    Main()()

+ 59 - 72
scripts/otp.py

@@ -32,12 +32,13 @@ OTP_DISPLAYS = {
     "mgg": 0x02,
     "mgg": 0x02,
 }
 }
 
 
+from flipper.app import App
+from flipper.cube import CubeProgrammer
 
 
-class Main:
-    def __init__(self):
-        # command args
-        self.parser = argparse.ArgumentParser()
-        self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
+
+class Main(App):
+    def init(self):
+        # SubParsers
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
         # Generate All
         # Generate All
         self.parser_generate_all = self.subparsers.add_parser(
         self.parser_generate_all = self.subparsers.add_parser(
@@ -73,21 +74,6 @@ class Main:
         self.logger = logging.getLogger()
         self.logger = logging.getLogger()
         self.timestamp = datetime.datetime.now().timestamp()
         self.timestamp = datetime.datetime.now().timestamp()
 
 
-    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 _add_swd_args(self, parser):
     def _add_swd_args(self, parser):
         parser.add_argument(
         parser.add_argument(
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
             "--port", type=str, help="Port to connect: swd or usb1", default="swd"
@@ -153,89 +139,90 @@ class Main:
         )
         )
 
 
     def generate_all(self):
     def generate_all(self):
-        self.logger.debug(f"Generating OTP")
+        self.logger.info(f"Generating OTP")
         self._process_first_args()
         self._process_first_args()
         self._process_second_args()
         self._process_second_args()
         open(f"{self.args.file}_first.bin", "wb").write(self._pack_first())
         open(f"{self.args.file}_first.bin", "wb").write(self._pack_first())
         open(f"{self.args.file}_second.bin", "wb").write(self._pack_second())
         open(f"{self.args.file}_second.bin", "wb").write(self._pack_second())
+        self.logger.info(
+            f"Generated files: {self.args.file}_first.bin and {self.args.file}_second.bin"
+        )
+
+        return 0
 
 
     def flash_first(self):
     def flash_first(self):
-        self.logger.debug(f"Flashing first block of OTP")
+        self.logger.info(f"Flashing first block of OTP")
 
 
         self._process_first_args()
         self._process_first_args()
 
 
         filename = f"otp_unknown_first_{self.timestamp}.bin"
         filename = f"otp_unknown_first_{self.timestamp}.bin"
-        file = open(filename, "wb")
-        file.write(self._pack_first())
-        file.close()
 
 
-        self._flash_bin("0x1FFF7000", filename)
+        try:
+            self.logger.info(f"Packing binary data")
+            file = open(filename, "wb")
+            file.write(self._pack_first())
+            file.close()
+            self.logger.info(f"Flashing OTP")
+            cp = CubeProgrammer(self.args.port)
+            cp.flashBin("0x1FFF7000", filename)
+            cp.resetTarget()
+            self.logger.info(f"Flashed Successfully")
+            os.remove(filename)
+        except Exception as e:
+            self.logger.exception(e)
+            return 0
 
 
-        os.remove(filename)
+        return 1
 
 
     def flash_second(self):
     def flash_second(self):
-        self.logger.debug(f"Flashing second block of OTP")
+        self.logger.info(f"Flashing second block of OTP")
 
 
         self._process_second_args()
         self._process_second_args()
 
 
         filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
         filename = f"otp_{self.args.name}_second_{self.timestamp}.bin"
-        file = open(filename, "wb")
-        file.write(self._pack_second())
-        file.close()
 
 
-        self._flash_bin("0x1FFF7010", filename)
+        try:
+            self.logger.info(f"Packing binary data")
+            file = open(filename, "wb")
+            file.write(self._pack_second())
+            file.close()
+            self.logger.info(f"Flashing OTP")
+            cp = CubeProgrammer(self.args.port)
+            cp.flashBin("0x1FFF7010", filename)
+            cp.resetTarget()
+            self.logger.info(f"Flashed Successfully")
+            os.remove(filename)
+        except Exception as e:
+            self.logger.exception(e)
+            return 1
 
 
-        os.remove(filename)
+        return 0
 
 
     def flash_all(self):
     def flash_all(self):
-        self.logger.debug(f"Flashing OTP")
+        self.logger.info(f"Flashing OTP")
 
 
         self._process_first_args()
         self._process_first_args()
         self._process_second_args()
         self._process_second_args()
 
 
         filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
         filename = f"otp_{self.args.name}_whole_{self.timestamp}.bin"
-        file = open(filename, "wb")
-        file.write(self._pack_first())
-        file.write(self._pack_second())
-        file.close()
-
-        self._flash_bin("0x1FFF7000", filename)
-
-        os.remove(filename)
 
 
-    def _flash_bin(self, address, filename):
-        self.logger.debug(f"Programming {filename} at {address}")
         try:
         try:
-            output = subprocess.check_output(
-                [
-                    "STM32_Programmer_CLI",
-                    "-q",
-                    "-c",
-                    f"port={self.args.port}",
-                    "-d",
-                    filename,
-                    f"{address}",
-                ]
-            )
-            assert output
-            self.logger.info(f"Success")
-        except subprocess.CalledProcessError as e:
-            self.logger.error(e.output.decode())
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
-            return
+            self.logger.info(f"Packing binary data")
+            file = open(filename, "wb")
+            file.write(self._pack_first())
+            file.write(self._pack_second())
+            file.close()
+            self.logger.info(f"Flashing OTP")
+            cp = CubeProgrammer(self.args.port)
+            cp.flashBin("0x1FFF7000", filename)
+            cp.resetTarget()
+            self.logger.info(f"Flashed Successfully")
+            os.remove(filename)
         except Exception as e:
         except Exception as e:
-            self.logger.error(f"Failed to call STM32_Programmer_CLI")
             self.logger.exception(e)
             self.logger.exception(e)
-            return
-        # reboot
-        subprocess.check_output(
-            [
-                "STM32_Programmer_CLI",
-                "-q",
-                "-c",
-                f"port={self.args.port}",
-            ]
-        )
+            return 1
+
+        return 0
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":