3333#include "esp_flash.h"
3434#include "esp_flash_internal.h"
3535
36- #define SUB_TYPE_ID (i ) (i & 0x0F)
36+ #define OTA_SLOT (i ) (i & 0x0F)
3737#define ALIGN_UP (num , align ) (((num) + ((align) - 1)) & ~((align) - 1))
3838
3939/* Partial_data is word aligned so no reallocation is necessary for encrypted flash write */
@@ -539,6 +539,69 @@ static esp_err_t rewrite_ota_seq(esp_ota_select_entry_t *two_otadata, uint32_t s
539539 }
540540}
541541
542+ /**
543+ * @brief Calculate the next OTA sequence number that will boot the given OTA slot.
544+ *
545+ * Based on the ESP-IDF OTA boot scheme, the system selects the OTA slot to boot by:
546+ * boot_slot = (seq - 1) % ota_app_count
547+ *
548+ * This function determines the required seq value that would cause the given ota_slot_idx
549+ * to be selected on next boot.
550+ *
551+ * @param current_seq Current active OTA sequence number
552+ * @param ota_slot_idx Target OTA slot index (0-based)
553+ * @param ota_app_count Total number of OTA slots
554+ *
555+ * @return New sequence number that will result in booting ota_slot_idx
556+ */
557+ static uint32_t compute_ota_seq_for_target_slot (uint32_t current_seq , uint32_t ota_slot_idx , uint8_t ota_app_count )
558+ {
559+ if (ota_app_count == 0 ) {
560+ return 0 ;
561+ }
562+ /* ESP-IDF stores OTA boot information in the OTA data partition, which consists of two sectors.
563+ * Each sector holds an esp_ota_select_entry_t structure: otadata[0] and otadata[1].
564+ * These structures record the OTA sequence number (ota_seq) used to determine the current boot partition.
565+ *
566+ * Boot selection logic:
567+ * - If both otadata[0].ota_seq and otadata[1].ota_seq are 0xFFFFFFFF (invalid), it is the initial state:
568+ * → Boot the factory app, if it exists.
569+ * → Otherwise, fall back to booting ota[0].
570+ *
571+ * - If both otadata entries have valid sequence numbers and CRCs:
572+ * → Choose the higher sequence number (max_seq).
573+ * → Determine the OTA partition for boot (or running partition) using:
574+ * running_ota_slot = (max_seq - 1) % ota_app_count
575+ * where ota_app_count is the total number of OTA app partitions.
576+ *
577+ * Example:
578+ * otadata[0].ota_seq = 4
579+ * otadata[1].ota_seq = 5
580+ * ota_app_count = 8 (available OTA slots: ota_0 to ota_7)
581+ * → max_seq = 5
582+ * → running slot = (5 - 1) % 8 = 4
583+ * → So ota_4 is currently running
584+ *
585+ * If you want to switch to boot a different OTA slot (e.g., ota_7):
586+ * → You need to compute a new sequence number such that:
587+ * (new_seq - 1) % ota_app_count == 7
588+ * while ensuring new_seq > current_seq.
589+ *
590+ * General formula:
591+ * x = current OTA slot ID
592+ * ota_slot_idx = desired OTA slot ID
593+ * seq = current ota_seq
594+ *
595+ * To find the next ota_seq that will boot ota_y, use:
596+ * new_seq = ((ota_slot_idx + 1) % ota_app_count) + ota_app_count * i;
597+ * // where i is the smallest non-negative integer such that new_seq > seq
598+ */
599+ uint32_t i = 0 ;
600+ uint32_t base = (ota_slot_idx + 1 ) % ota_app_count ;
601+ while (current_seq > (base + i * ota_app_count )) { i ++ ; };
602+ return base + i * ota_app_count ;
603+ }
604+
542605uint8_t esp_ota_get_app_partition_count (void )
543606{
544607 uint16_t ota_app_count = 0 ;
@@ -549,6 +612,30 @@ uint8_t esp_ota_get_app_partition_count(void)
549612 return ota_app_count ;
550613}
551614
615+ /**
616+ * @brief Update the OTA data partition to set the given OTA app subtype as the next boot target.
617+ *
618+ * ESP-IDF uses the OTA data partition to track which OTA app should boot.
619+ * This partition contains two entries (otadata[0] and otadata[1]), each storing an esp_ota_select_entry_t struct,
620+ * which includes the OTA sequence number (ota_seq).
621+ *
622+ * On boot, the chip determines the current running OTA slot using:
623+ * current_slot = (max(ota_seq) - 1) % ota_app_count
624+ *
625+ * This function updates the OTA data to switch the next boot to the partition with the given subtype.
626+ *
627+ * Behavior:
628+ * - If the currently selected OTA slot already matches the requested subtype,
629+ * only the state field is updated (e.g., to mark the app as newly downloaded).
630+ * - Otherwise, it calculates the next valid ota_seq that will cause the bootloader to select
631+ * the requested OTA slot on reboot, and writes it to the inactive OTA data sector.
632+ *
633+ * @param subtype The OTA partition subtype (e.g., ESP_PARTITION_SUBTYPE_APP_OTA_0, ..._OTA_1, ...)
634+ * @return
635+ * - ESP_OK if update was successful
636+ * - ESP_ERR_NOT_FOUND if OTA data partition not found
637+ * - ESP_ERR_INVALID_ARG if subtype is out of range
638+ */
552639static esp_err_t esp_rewrite_ota_data (esp_partition_subtype_t subtype )
553640{
554641 esp_ota_select_entry_t otadata [2 ];
@@ -558,42 +645,31 @@ static esp_err_t esp_rewrite_ota_data(esp_partition_subtype_t subtype)
558645 }
559646
560647 uint8_t ota_app_count = esp_ota_get_app_partition_count ();
561- if (SUB_TYPE_ID (subtype ) >= ota_app_count ) {
648+ if (OTA_SLOT (subtype ) >= ota_app_count ) {
562649 return ESP_ERR_INVALID_ARG ;
563650 }
564-
565- //esp32_idf use two sector for store information about which partition is running
566- //it defined the two sector as ota data partition,two structure esp_ota_select_entry_t is saved in the two sector
567- //named data in first sector as otadata[0], second sector data as otadata[1]
568- //e.g.
569- //if otadata[0].ota_seq == otadata[1].ota_seq == 0xFFFFFFFF,means ota info partition is in init status
570- //so it will boot factory application(if there is),if there's no factory application,it will boot ota[0] application
571- //if otadata[0].ota_seq != 0 and otadata[1].ota_seq != 0,it will choose a max seq ,and get value of max_seq%max_ota_app_number
572- //and boot a subtype (mask 0x0F) value is (max_seq - 1)%max_ota_app_number,so if want switch to run ota[x],can use next formulas.
573- //for example, if otadata[0].ota_seq = 4, otadata[1].ota_seq = 5, and there are 8 ota application,
574- //current running is (5-1)%8 = 4,running ota[4],so if we want to switch to run ota[7],
575- //we should add otadata[0].ota_seq (is 4) to 4 ,(8-1)%8=7,then it will boot ota[7]
576- //if A=(B - C)%D
577- //then B=(A + C)%D + D*n ,n= (0,1,2...)
578- //so current ota app sub type id is x , dest bin subtype is y,total ota app count is n
579- //seq will add (x + n*1 + 1 - seq)%n
580-
581651 int active_otadata = bootloader_common_get_active_otadata (otadata );
652+ int next_otadata ;
653+ uint32_t new_seq ;
582654 if (active_otadata != -1 ) {
583- uint32_t seq = otadata [active_otadata ].ota_seq ;
584- uint32_t i = 0 ;
585- while (seq > (SUB_TYPE_ID (subtype ) + 1 ) % ota_app_count + i * ota_app_count ) {
586- i ++ ;
655+ uint32_t ota_slot = (otadata [active_otadata ].ota_seq - 1 ) % ota_app_count ;
656+ if (ota_slot == OTA_SLOT (subtype )) {
657+ // ota_data is already valid and points to the correct OTA slot.
658+ // So after reboot the requested partition will be selected for boot.
659+ // Only update the ota_state of the requested partition.
660+ next_otadata = active_otadata ;
661+ new_seq = otadata [active_otadata ].ota_seq ;
662+ } else {
663+ next_otadata = (~active_otadata ) & 1 ; // if 0 -> will be next 1. and if 1 -> will be next 0.
664+ new_seq = compute_ota_seq_for_target_slot (otadata [active_otadata ].ota_seq , OTA_SLOT (subtype ), ota_app_count );
587665 }
588- int next_otadata = (~active_otadata )& 1 ; // if 0 -> will be next 1. and if 1 -> will be next 0.
589- otadata [next_otadata ].ota_state = set_new_state_otadata ();
590- return rewrite_ota_seq (otadata , (SUB_TYPE_ID (subtype ) + 1 ) % ota_app_count + i * ota_app_count , next_otadata , otadata_partition );
591666 } else {
592667 /* Both OTA slots are invalid, probably because unformatted... */
593- int next_otadata = 0 ;
594- otadata [next_otadata ].ota_state = set_new_state_otadata ();
595- return rewrite_ota_seq (otadata , SUB_TYPE_ID (subtype ) + 1 , next_otadata , otadata_partition );
668+ next_otadata = 0 ;
669+ new_seq = OTA_SLOT (subtype ) + 1 ;
596670 }
671+ otadata [next_otadata ].ota_state = set_new_state_otadata ();
672+ return rewrite_ota_seq (otadata , new_seq , next_otadata , otadata_partition );
597673}
598674
599675esp_err_t esp_ota_set_boot_partition (const esp_partition_t * partition )
0 commit comments