[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.
This commit is contained in:
あく 2021-10-21 15:24:34 +03:00 committed by GitHub
parent 4997b56498
commit 2751440193
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 1246 additions and 295 deletions

71
Makefile Normal file
View File

@ -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

View File

@ -70,6 +70,18 @@ One liner: `./flash_core1_main.sh`
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
```sh

View File

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

View File

@ -44,7 +44,7 @@ void desktop_first_start_render(Canvas* canvas, void* model) {
"%s %s%s",
"I am",
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);
elements_multiline_text_framed(canvas, 60, 17, buf);
} else if(m->page == 6) {

View File

@ -12,7 +12,7 @@ Canvas* canvas_init() {
furi_hal_power_insomnia_enter();
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
u8g2_InitDisplay(&canvas->fb);

View File

@ -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;
}

View File

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

View File

@ -189,7 +189,7 @@ void target_display_init() {
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
// Initialize
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);
// Create payload
u8g2_ClearBuffer(&fb);

View File

@ -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;
}

View File

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

View File

@ -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);

View File

@ -189,7 +189,7 @@ void target_display_init() {
hal_gpio_init_simple(&gpio_display_di, GpioModeOutputPushPull);
// Initialize
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);
// Create payload
u8g2_ClearBuffer(&fb);

View File

@ -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

View File

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

View File

@ -185,7 +185,7 @@ uint8_t u8x8_d_st756x_common(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *a
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;
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);
}
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 */
if (u8x8_d_st756x_common(u8x8, msg, arg_int, arg_ptr) == 0) {
/* 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;
case U8X8_MSG_DISPLAY_INIT:
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;
case U8X8_MSG_DISPLAY_SET_FLIP_MODE:
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;
}
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 *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);
u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}

View File

@ -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);
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);

View File

@ -140,7 +140,7 @@ generate_cscope_db:
@cscope -b -k -i $(OBJ_DIR)/source.list -f $(OBJ_DIR)/cscope.out
@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)
endif

View File

@ -1,6 +1,6 @@
# 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).
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:
- OTP (Only on empty MCU)
- Core2 firmware
- Core1 firmware
- Core1 and Core2 firmware flashing
- Option Bytes
## Otp flashing
!!! 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.
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.
Also display type, region and etc...
!!! 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

152
scripts/flash.py Executable file
View File

@ -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()()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
scripts/flipper/app.py Normal file
View File

@ -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
scripts/flipper/cube.py Normal file
View File

@ -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([])

View File

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

View File

@ -6,12 +6,12 @@ import subprocess
import sys
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.parser_check = self.subparsers.add_parser(
"check", help="Check Option Bytes"
@ -20,39 +20,16 @@ class Main:
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
)
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.add_argument(
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
)
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()
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")
file_path = os.path.join(os.path.dirname(sys.argv[0]), "ob.data")
file = open(file_path, "r")
@ -62,47 +39,8 @@ class Main:
def check(self):
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")
return 0
else:
@ -111,34 +49,14 @@ class Main:
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",
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__":
return_code = Main()()
exit(return_code)
Main()()

View File

@ -32,12 +32,13 @@ OTP_DISPLAYS = {
"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")
# Generate All
self.parser_generate_all = self.subparsers.add_parser(
@ -73,21 +74,6 @@ class Main:
self.logger = logging.getLogger()
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):
parser.add_argument(
"--port", type=str, help="Port to connect: swd or usb1", default="swd"
@ -153,89 +139,90 @@ class Main:
)
def generate_all(self):
self.logger.debug(f"Generating OTP")
self.logger.info(f"Generating OTP")
self._process_first_args()
self._process_second_args()
open(f"{self.args.file}_first.bin", "wb").write(self._pack_first())
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):
self.logger.debug(f"Flashing first block of OTP")
self.logger.info(f"Flashing first block of OTP")
self._process_first_args()
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):
self.logger.debug(f"Flashing second block of OTP")
self.logger.info(f"Flashing second block of OTP")
self._process_second_args()
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):
self.logger.debug(f"Flashing OTP")
self.logger.info(f"Flashing OTP")
self._process_first_args()
self._process_second_args()
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:
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:
self.logger.error(f"Failed to call STM32_Programmer_CLI")
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__":