Browse Source

[FL-1945] Firmware, Scripts, Cli: add OTPv2, alternative displays support and 2-step OTP programming. #764

あく 4 năm trước cách đây
mục cha
commit
0e14545d48

+ 10 - 6
applications/cli/cli_commands.c

@@ -57,10 +57,6 @@ static const uint8_t enclave_signature_expected[ENCLAVE_SIGNATURE_KEY_SLOTS][ENC
 void cli_command_device_info(Cli* cli, string_t args, void* context) {
 void cli_command_device_info(Cli* cli, string_t args, void* context) {
     // Model name
     // Model name
     printf("hardware_model      : %s\r\n", furi_hal_version_get_model_name());
     printf("hardware_model      : %s\r\n", furi_hal_version_get_model_name());
-    const char* name = furi_hal_version_get_name_ptr();
-    if(name) {
-        printf("hardware_name       : %s\r\n", name);
-    }
 
 
     // Unique ID
     // Unique ID
     printf("hardware_uid        : ");
     printf("hardware_uid        : ");
@@ -70,16 +66,24 @@ void cli_command_device_info(Cli* cli, string_t args, void* context) {
     }
     }
     printf("\r\n");
     printf("\r\n");
 
 
+    // OTP Revision
+    printf("hardware_otp_ver    : %d\r\n", furi_hal_version_get_otp_version());
+    printf("hardware_timestamp  : %lu\r\n", furi_hal_version_get_hw_timestamp());
+
     // Board Revision
     // Board Revision
     printf("hardware_ver        : %d\r\n", furi_hal_version_get_hw_version());
     printf("hardware_ver        : %d\r\n", furi_hal_version_get_hw_version());
     printf("hardware_target     : %d\r\n", furi_hal_version_get_hw_target());
     printf("hardware_target     : %d\r\n", furi_hal_version_get_hw_target());
     printf("hardware_body       : %d\r\n", furi_hal_version_get_hw_body());
     printf("hardware_body       : %d\r\n", furi_hal_version_get_hw_body());
     printf("hardware_connect    : %d\r\n", furi_hal_version_get_hw_connect());
     printf("hardware_connect    : %d\r\n", furi_hal_version_get_hw_connect());
-    printf("hardware_timestamp  : %lu\r\n", furi_hal_version_get_hw_timestamp());
+    printf("hardware_display    : %d\r\n", furi_hal_version_get_hw_display());
 
 
-    // Color and Region
+    // Board Personification
     printf("hardware_color      : %d\r\n", furi_hal_version_get_hw_color());
     printf("hardware_color      : %d\r\n", furi_hal_version_get_hw_color());
     printf("hardware_region     : %d\r\n", furi_hal_version_get_hw_region());
     printf("hardware_region     : %d\r\n", furi_hal_version_get_hw_region());
+    const char* name = furi_hal_version_get_name_ptr();
+    if(name) {
+        printf("hardware_name       : %s\r\n", name);
+    }
 
 
     // Bootloader Version
     // Bootloader Version
     const Version* boot_version = furi_hal_version_get_boot_version();
     const Version* boot_version = furi_hal_version_get_boot_version();

+ 84 - 28
firmware/targets/f6/furi-hal/furi-hal-version.c

@@ -10,13 +10,6 @@
 #define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
 #define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
 #define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
 #define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
 
 
-/** OTP Versions enum */
-typedef enum {
-    FuriHalVersionOtpVersion0=0x00,
-    FuriHalVersionOtpVersion1=0x01,
-    FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
-    FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
-} FuriHalVersionOtpVersion;
 
 
 /** OTP V0 Structure: prototypes and early EVT */
 /** OTP V0 Structure: prototypes and early EVT */
 typedef struct {
 typedef struct {
@@ -28,7 +21,7 @@ typedef struct {
     char name[FURI_HAL_VERSION_NAME_LENGTH];
     char name[FURI_HAL_VERSION_NAME_LENGTH];
 } FuriHalVersionOTPv0;
 } FuriHalVersionOTPv0;
 
 
-/** OTP V1 Structure: late EVT, DVT, PVT, Production */
+/** OTP V1 Structure: late EVT, DVT */
 typedef struct {
 typedef struct {
     /* First 64 bits: header */
     /* First 64 bits: header */
     uint16_t header_magic;
     uint16_t header_magic;
@@ -49,10 +42,35 @@ typedef struct {
     char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
     char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
 } FuriHalVersionOTPv1;
 } FuriHalVersionOTPv1;
 
 
-/** Represenation Model: */
+/** OTP V2 Structure: DVT2, PVT, Production */
 typedef struct {
 typedef struct {
-    FuriHalVersionOtpVersion otp_version;
+    /* 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;
     uint32_t timestamp;
 
 
     uint8_t board_version; /** Board version */
     uint8_t board_version; /** Board version */
@@ -61,6 +79,7 @@ typedef struct {
     uint8_t board_connect; /** Board interconnect */
     uint8_t board_connect; /** Board interconnect */
     uint8_t board_color; /** Board color */
     uint8_t board_color; /** Board color */
     uint8_t board_region; /** Board region */
     uint8_t board_region; /** Board region */
+    uint8_t board_display; /** Board display */
 
 
     char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
     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 */
     char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];  /** device name for special needs */
@@ -69,20 +88,6 @@ typedef struct {
 
 
 static FuriHalVersion furi_hal_version = {0};
 static FuriHalVersion furi_hal_version = {0};
 
 
-static 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) {
-            return FuriHalVersionOtpVersion1;
-        } else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
-            return FuriHalVersionOtpVersion0;
-        } else {
-            return FuriHalVersionOtpVersionUnknown;
-        }
-    }
-}
-
 static void furi_hal_version_set_name(const char* name) {
 static void furi_hal_version_set_name(const char* name) {
     if(name != NULL) {
     if(name != NULL) {
         strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
         strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
@@ -124,8 +129,6 @@ static void furi_hal_version_load_otp_v0() {
     furi_hal_version.board_target = otp->board_target;
     furi_hal_version.board_target = otp->board_target;
     furi_hal_version.board_body = otp->board_body;
     furi_hal_version.board_body = otp->board_body;
     furi_hal_version.board_connect = otp->board_connect;
     furi_hal_version.board_connect = otp->board_connect;
-    furi_hal_version.board_color = 0;
-    furi_hal_version.board_region = 0;
 
 
     furi_hal_version_set_name(otp->name);
     furi_hal_version_set_name(otp->name);
 }
 }
@@ -144,9 +147,33 @@ static void furi_hal_version_load_otp_v1() {
     furi_hal_version_set_name(otp->name);
     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() {
 void furi_hal_version_init() {
-    furi_hal_version.otp_version = furi_hal_version_get_otp_version();
-    switch(furi_hal_version.otp_version) {
+    switch(furi_hal_version_get_otp_version()) {
         case FuriHalVersionOtpVersionUnknown:
         case FuriHalVersionOtpVersionUnknown:
             furi_hal_version_load_otp_default();
             furi_hal_version_load_otp_default();
         break;
         break;
@@ -159,6 +186,9 @@ void furi_hal_version_init() {
         case FuriHalVersionOtpVersion1:
         case FuriHalVersionOtpVersion1:
             furi_hal_version_load_otp_v1();
             furi_hal_version_load_otp_v1();
         break;
         break;
+        case FuriHalVersionOtpVersion2:
+            furi_hal_version_load_otp_v2();
+        break;
         default: furi_crash(NULL);
         default: furi_crash(NULL);
     }
     }
     FURI_LOG_I("FuriHalVersion", "Init OK");
     FURI_LOG_I("FuriHalVersion", "Init OK");
@@ -172,6 +202,28 @@ const char* furi_hal_version_get_model_name() {
     return "Flipper Zero";
     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() {
 const uint8_t furi_hal_version_get_hw_version() {
     return furi_hal_version.board_version;
     return furi_hal_version.board_version;
 }
 }
@@ -196,6 +248,10 @@ const FuriHalVersionRegion furi_hal_version_get_hw_region() {
     return furi_hal_version.board_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() {
 const uint32_t furi_hal_version_get_hw_timestamp() {
     return furi_hal_version.timestamp;
     return furi_hal_version.timestamp;
 }
 }

+ 84 - 28
firmware/targets/f7/furi-hal/furi-hal-version.c

@@ -10,13 +10,6 @@
 #define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
 #define FURI_HAL_VERSION_OTP_HEADER_MAGIC 0xBABE
 #define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
 #define FURI_HAL_VERSION_OTP_ADDRESS OTP_AREA_BASE
 
 
-/** OTP Versions enum */
-typedef enum {
-    FuriHalVersionOtpVersion0=0x00,
-    FuriHalVersionOtpVersion1=0x01,
-    FuriHalVersionOtpVersionEmpty=0xFFFFFFFE,
-    FuriHalVersionOtpVersionUnknown=0xFFFFFFFF,
-} FuriHalVersionOtpVersion;
 
 
 /** OTP V0 Structure: prototypes and early EVT */
 /** OTP V0 Structure: prototypes and early EVT */
 typedef struct {
 typedef struct {
@@ -28,7 +21,7 @@ typedef struct {
     char name[FURI_HAL_VERSION_NAME_LENGTH];
     char name[FURI_HAL_VERSION_NAME_LENGTH];
 } FuriHalVersionOTPv0;
 } FuriHalVersionOTPv0;
 
 
-/** OTP V1 Structure: late EVT, DVT, PVT, Production */
+/** OTP V1 Structure: late EVT, DVT */
 typedef struct {
 typedef struct {
     /* First 64 bits: header */
     /* First 64 bits: header */
     uint16_t header_magic;
     uint16_t header_magic;
@@ -49,10 +42,35 @@ typedef struct {
     char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
     char name[FURI_HAL_VERSION_NAME_LENGTH]; /** Unique Device Name */
 } FuriHalVersionOTPv1;
 } FuriHalVersionOTPv1;
 
 
-/** Represenation Model: */
+/** OTP V2 Structure: DVT2, PVT, Production */
 typedef struct {
 typedef struct {
-    FuriHalVersionOtpVersion otp_version;
+    /* 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;
     uint32_t timestamp;
 
 
     uint8_t board_version; /** Board version */
     uint8_t board_version; /** Board version */
@@ -61,6 +79,7 @@ typedef struct {
     uint8_t board_connect; /** Board interconnect */
     uint8_t board_connect; /** Board interconnect */
     uint8_t board_color; /** Board color */
     uint8_t board_color; /** Board color */
     uint8_t board_region; /** Board region */
     uint8_t board_region; /** Board region */
+    uint8_t board_display; /** Board display */
 
 
     char name[FURI_HAL_VERSION_ARRAY_NAME_LENGTH]; /** \0 terminated name */
     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 */
     char device_name[FURI_HAL_VERSION_DEVICE_NAME_LENGTH];  /** device name for special needs */
@@ -69,20 +88,6 @@ typedef struct {
 
 
 static FuriHalVersion furi_hal_version = {0};
 static FuriHalVersion furi_hal_version = {0};
 
 
-static 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) {
-            return FuriHalVersionOtpVersion1;
-        } else if (((FuriHalVersionOTPv0*)FURI_HAL_VERSION_OTP_ADDRESS)->board_version <= 10) {
-            return FuriHalVersionOtpVersion0;
-        } else {
-            return FuriHalVersionOtpVersionUnknown;
-        }
-    }
-}
-
 static void furi_hal_version_set_name(const char* name) {
 static void furi_hal_version_set_name(const char* name) {
     if(name != NULL) {
     if(name != NULL) {
         strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
         strlcpy(furi_hal_version.name, name, FURI_HAL_VERSION_ARRAY_NAME_LENGTH);
@@ -124,8 +129,6 @@ static void furi_hal_version_load_otp_v0() {
     furi_hal_version.board_target = otp->board_target;
     furi_hal_version.board_target = otp->board_target;
     furi_hal_version.board_body = otp->board_body;
     furi_hal_version.board_body = otp->board_body;
     furi_hal_version.board_connect = otp->board_connect;
     furi_hal_version.board_connect = otp->board_connect;
-    furi_hal_version.board_color = 0;
-    furi_hal_version.board_region = 0;
 
 
     furi_hal_version_set_name(otp->name);
     furi_hal_version_set_name(otp->name);
 }
 }
@@ -144,9 +147,33 @@ static void furi_hal_version_load_otp_v1() {
     furi_hal_version_set_name(otp->name);
     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() {
 void furi_hal_version_init() {
-    furi_hal_version.otp_version = furi_hal_version_get_otp_version();
-    switch(furi_hal_version.otp_version) {
+    switch(furi_hal_version_get_otp_version()) {
         case FuriHalVersionOtpVersionUnknown:
         case FuriHalVersionOtpVersionUnknown:
             furi_hal_version_load_otp_default();
             furi_hal_version_load_otp_default();
         break;
         break;
@@ -159,6 +186,9 @@ void furi_hal_version_init() {
         case FuriHalVersionOtpVersion1:
         case FuriHalVersionOtpVersion1:
             furi_hal_version_load_otp_v1();
             furi_hal_version_load_otp_v1();
         break;
         break;
+        case FuriHalVersionOtpVersion2:
+            furi_hal_version_load_otp_v2();
+        break;
         default: furi_crash(NULL);
         default: furi_crash(NULL);
     }
     }
     FURI_LOG_I("FuriHalVersion", "Init OK");
     FURI_LOG_I("FuriHalVersion", "Init OK");
@@ -172,6 +202,28 @@ const char* furi_hal_version_get_model_name() {
     return "Flipper Zero";
     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() {
 const uint8_t furi_hal_version_get_hw_version() {
     return furi_hal_version.board_version;
     return furi_hal_version.board_version;
 }
 }
@@ -196,6 +248,10 @@ const FuriHalVersionRegion furi_hal_version_get_hw_region() {
     return furi_hal_version.board_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() {
 const uint32_t furi_hal_version_get_hw_timestamp() {
     return furi_hal_version.timestamp;
     return furi_hal_version.timestamp;
 }
 }

+ 28 - 0
firmware/targets/furi-hal-include/furi-hal-version.h

@@ -19,6 +19,15 @@ extern "C" {
 /** BLE symbol + "Flipper " + name */
 /** BLE symbol + "Flipper " + name */
 #define FURI_HAL_VERSION_DEVICE_NAME_LENGTH (1 + 8 + FURI_HAL_VERSION_ARRAY_NAME_LENGTH)
 #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 */
 /** Device Colors */
 typedef enum {
 typedef enum {
     FuriHalVersionColorUnknown=0x00,
     FuriHalVersionColorUnknown=0x00,
@@ -34,6 +43,13 @@ typedef enum {
     FuriHalVersionRegionJp=0x03,
     FuriHalVersionRegionJp=0x03,
 } FuriHalVersionRegion;
 } FuriHalVersionRegion;
 
 
+/** Device Display */
+typedef enum {
+    FuriHalVersionDisplayUnknown=0x00,
+    FuriHalVersionDisplayErc=0x01,
+    FuriHalVersionDisplayMgg=0x02,
+} FuriHalVersionDisplay;
+
 /** Init flipper version
 /** Init flipper version
  */
  */
 void furi_hal_version_init();
 void furi_hal_version_init();
@@ -50,6 +66,12 @@ bool furi_hal_version_do_i_belong_here();
  */
  */
 const char* furi_hal_version_get_model_name();
 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
 /** Get hardware version
  *
  *
  * @return     Hardware Version
  * @return     Hardware Version
@@ -86,6 +108,12 @@ const uint8_t furi_hal_version_get_hw_connect();
  */
  */
 const FuriHalVersionRegion furi_hal_version_get_hw_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
 /** Get hardware timestamp
  *
  *
  * @return     Hardware Manufacture timestamp
  * @return     Hardware Manufacture timestamp

+ 116 - 36
scripts/otp.py

@@ -10,7 +10,7 @@ import struct
 import datetime
 import datetime
 
 
 OTP_MAGIC = 0xBABE
 OTP_MAGIC = 0xBABE
-OTP_VERSION = 0x01
+OTP_VERSION = 0x02
 OTP_RESERVED = 0x00
 OTP_RESERVED = 0x00
 
 
 OTP_COLORS = {
 OTP_COLORS = {
@@ -18,6 +18,7 @@ OTP_COLORS = {
     "black": 0x01,
     "black": 0x01,
     "white": 0x02,
     "white": 0x02,
 }
 }
+
 OTP_REGIONS = {
 OTP_REGIONS = {
     "unknown": 0x00,
     "unknown": 0x00,
     "eu_ru": 0x01,
     "eu_ru": 0x01,
@@ -25,7 +26,11 @@ OTP_REGIONS = {
     "jp": 0x03,
     "jp": 0x03,
 }
 }
 
 
-BOARD_RESERVED = 0x0000
+OTP_DISPLAYS = {
+    "unknown": 0x00,
+    "erc": 0x01,
+    "mgg": 0x02,
+}
 
 
 
 
 class Main:
 class Main:
@@ -34,22 +39,36 @@ class Main:
         self.parser = argparse.ArgumentParser()
         self.parser = argparse.ArgumentParser()
         self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
         self.parser.add_argument("-d", "--debug", action="store_true", help="Debug")
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
         self.subparsers = self.parser.add_subparsers(help="sub-command help")
-        # Generate
-        self.parser_generate = self.subparsers.add_parser(
+        # Generate All
+        self.parser_generate_all = self.subparsers.add_parser(
             "generate", help="Generate OTP binary"
             "generate", help="Generate OTP binary"
         )
         )
-        self._add_args(self.parser_generate)
-        self.parser_generate.add_argument("file", help="Output file")
-        self.parser_generate.set_defaults(func=self.generate)
-        # Flash
-        self.parser_flash = self.subparsers.add_parser(
-            "flash", help="Flash OTP to device"
+        self._add_first_args(self.parser_generate_all)
+        self._add_second_args(self.parser_generate_all)
+        self.parser_generate_all.add_argument("file", help="Output file")
+        self.parser_generate_all.set_defaults(func=self.generate_all)
+        # Flash First
+        self.parser_flash_first = self.subparsers.add_parser(
+            "flash_first", help="Flash first block of OTP to device"
         )
         )
-        self._add_args(self.parser_flash)
-        self.parser_flash.add_argument(
-            "--port", type=str, help="Port to connect: swd or usb1", default="swd"
+        self._add_swd_args(self.parser_flash_first)
+        self._add_first_args(self.parser_flash_first)
+        self.parser_flash_first.set_defaults(func=self.flash_first)
+        # Flash Second
+        self.parser_flash_second = self.subparsers.add_parser(
+            "flash_second", help="Flash second block of OTP to device"
+        )
+        self._add_swd_args(self.parser_flash_second)
+        self._add_second_args(self.parser_flash_second)
+        self.parser_flash_second.set_defaults(func=self.flash_second)
+        # Flash All
+        self.parser_flash_all = self.subparsers.add_parser(
+            "flash_all", help="Flash OTP to device"
         )
         )
-        self.parser_flash.set_defaults(func=self.flash)
+        self._add_swd_args(self.parser_flash_all)
+        self._add_first_args(self.parser_flash_all)
+        self._add_second_args(self.parser_flash_all)
+        self.parser_flash_all.set_defaults(func=self.flash_all)
         # logging
         # logging
         self.logger = logging.getLogger()
         self.logger = logging.getLogger()
         self.timestamp = datetime.datetime.now().timestamp()
         self.timestamp = datetime.datetime.now().timestamp()
@@ -69,16 +88,29 @@ class Main:
         # execute requested function
         # execute requested function
         self.args.func()
         self.args.func()
 
 
-    def _add_args(self, parser):
-        parser.add_argument("--version", type=int, help="Version", default=11)
-        parser.add_argument("--firmware", type=int, help="Firmware", default=7)
-        parser.add_argument("--body", type=int, help="Body", default=9)
-        parser.add_argument("--connect", type=int, help="Connect", default=6)
-        parser.add_argument("--color", type=str, help="Color", default="unknown")
-        parser.add_argument("--region", type=str, help="Region", default="unknown")
+    def _add_swd_args(self, parser):
+        parser.add_argument(
+            "--port", type=str, help="Port to connect: swd or usb1", default="swd"
+        )
+
+    def _add_first_args(self, parser):
+        parser.add_argument("--version", type=int, help="Version", required=True)
+        parser.add_argument("--firmware", type=int, help="Firmware", required=True)
+        parser.add_argument("--body", type=int, help="Body", required=True)
+        parser.add_argument("--connect", type=int, help="Connect", required=True)
+        parser.add_argument("--display", type=str, help="Display", required=True)
+
+    def _add_second_args(self, parser):
+        parser.add_argument("--color", type=str, help="Color", required=True)
+        parser.add_argument("--region", type=str, help="Region", required=True)
         parser.add_argument("--name", type=str, help="Name", required=True)
         parser.add_argument("--name", type=str, help="Name", required=True)
 
 
-    def _process_args(self):
+    def _process_first_args(self):
+        if self.args.display not in OTP_DISPLAYS:
+            self.parser.error(f"Invalid display. Use one of {OTP_DISPLAYS.keys()}")
+        self.args.display = OTP_DISPLAYS[self.args.display]
+
+    def _process_second_args(self):
         if self.args.color not in OTP_COLORS:
         if self.args.color not in OTP_COLORS:
             self.parser.error(f"Invalid color. Use one of {OTP_COLORS.keys()}")
             self.parser.error(f"Invalid color. Use one of {OTP_COLORS.keys()}")
         self.args.color = OTP_COLORS[self.args.color]
         self.args.color = OTP_COLORS[self.args.color]
@@ -94,9 +126,9 @@ class Main:
                 "Name contains incorrect symbols. Only a-zA-Z0-9 allowed."
                 "Name contains incorrect symbols. Only a-zA-Z0-9 allowed."
             )
             )
 
 
-    def _pack_struct(self):
+    def _pack_first(self):
         return struct.pack(
         return struct.pack(
-            "<" "HBBL" "BBBBBBH" "8s",
+            "<" "HBBL" "BBBBBBH",
             OTP_MAGIC,
             OTP_MAGIC,
             OTP_VERSION,
             OTP_VERSION,
             OTP_RESERVED,
             OTP_RESERVED,
@@ -105,25 +137,74 @@ class Main:
             self.args.firmware,
             self.args.firmware,
             self.args.body,
             self.args.body,
             self.args.connect,
             self.args.connect,
+            self.args.display,
+            OTP_RESERVED,
+            OTP_RESERVED,
+        )
+
+    def _pack_second(self):
+        return struct.pack(
+            "<" "BBHL" "8s",
             self.args.color,
             self.args.color,
             self.args.region,
             self.args.region,
-            BOARD_RESERVED,
+            OTP_RESERVED,
+            OTP_RESERVED,
             self.args.name.encode("ascii"),
             self.args.name.encode("ascii"),
         )
         )
 
 
-    def generate(self):
+    def generate_all(self):
         self.logger.debug(f"Generating OTP")
         self.logger.debug(f"Generating OTP")
-        self._process_args()
-        data = self._pack_struct()
-        open(self.args.file, "wb").write(data)
+        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())
+
+    def flash_first(self):
+        self.logger.debug(f"Flashing first block of OTP")
 
 
-    def flash(self):
+        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)
+
+        os.remove(filename)
+
+    def flash_second(self):
+        self.logger.debug(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)
+
+        os.remove(filename)
+
+    def flash_all(self):
         self.logger.debug(f"Flashing OTP")
         self.logger.debug(f"Flashing OTP")
-        self._process_args()
-        data = self._pack_struct()
-        filename = f"otp_{self.args.name}_{self.timestamp}.bin"
-        open(filename, "wb").write(data)
 
 
+        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:
         try:
             output = subprocess.check_output(
             output = subprocess.check_output(
                 [
                 [
@@ -133,7 +214,7 @@ class Main:
                     f"port={self.args.port}",
                     f"port={self.args.port}",
                     "-d",
                     "-d",
                     filename,
                     filename,
-                    "0x1FFF7000",
+                    f"{address}",
                 ]
                 ]
             )
             )
             assert output
             assert output
@@ -155,7 +236,6 @@ class Main:
                 f"port={self.args.port}",
                 f"port={self.args.port}",
             ]
             ]
         )
         )
-        os.remove(filename)
 
 
 
 
 if __name__ == "__main__":
 if __name__ == "__main__":