Skip to main content

esp_bootloader_esp_idf/
ota_updater.rs

1//! # A more convenient way to access Over The Air Updates (OTA) functionality.
2
3use crate::{
4    ota::OtaImageState,
5    partitions::{AppPartitionSubType, Error, FlashRegion, FlashStorage, PartitionTable},
6};
7
8/// This can be used as more convenient - yet less flexible, way to do OTA updates.
9///
10/// If you need lower level access see [crate::ota::Ota]
11#[derive(Debug)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct OtaUpdater<'a, 'd> {
14    flash: &'a mut FlashStorage<'d>,
15    pt: PartitionTable<'a>,
16    ota_count: usize,
17}
18
19impl<'a, 'd> OtaUpdater<'a, 'd> {
20    /// Create a new instance of [OtaUpdater].
21    ///
22    /// # Errors
23    /// [Error::Invalid] if no OTA data partition or less than two OTA app partition were found.
24    pub fn new(
25        flash: &'a mut FlashStorage<'d>,
26        buffer: &'a mut [u8; crate::partitions::PARTITION_TABLE_MAX_LEN],
27    ) -> Result<Self, Error> {
28        let pt = crate::partitions::read_partition_table(flash, buffer)?;
29
30        let mut ota_count = 0;
31        let mut has_ota_data = false;
32        for part in pt.iter() {
33            match part.partition_type() {
34                crate::partitions::PartitionType::App(subtype)
35                    if subtype != crate::partitions::AppPartitionSubType::Factory
36                        && subtype != crate::partitions::AppPartitionSubType::Test =>
37                {
38                    ota_count += 1;
39                }
40                crate::partitions::PartitionType::Data(
41                    crate::partitions::DataPartitionSubType::Ota,
42                ) => {
43                    has_ota_data = true;
44                }
45                _ => {}
46            }
47        }
48
49        if !has_ota_data {
50            return Err(Error::Invalid);
51        }
52
53        if ota_count < 2 {
54            return Err(Error::Invalid);
55        }
56
57        Ok(Self {
58            flash,
59            pt,
60            ota_count,
61        })
62    }
63
64    /// Returns a [`crate::ota::Ota`] for accessing the OTA-data partition.
65    ///
66    /// # Errors
67    /// [Error::Invalid] if no OTA data partition was found.
68    pub fn ota_data(&mut self) -> Result<crate::ota::Ota<'_, 'd>, Error> {
69        let ota_part = self
70            .pt
71            .find_partition(crate::partitions::PartitionType::Data(
72                crate::partitions::DataPartitionSubType::Ota,
73            ))?;
74        if let Some(ota_part) = ota_part {
75            let ota_part = ota_part.as_flash_region(self.flash);
76            let ota = crate::ota::Ota::new(ota_part, self.ota_count)?;
77            Ok(ota)
78        } else {
79            Err(Error::Invalid)
80        }
81    }
82
83    fn next_ota_part(&mut self) -> Result<crate::partitions::AppPartitionSubType, Error> {
84        let current = self.selected_partition()?;
85        let next = match current {
86            AppPartitionSubType::Factory => AppPartitionSubType::Ota0,
87            _ => AppPartitionSubType::from_ota_app_number(
88                (current.ota_app_number() + 1) % self.ota_count as u8,
89            )?,
90        };
91
92        // make sure we don't select the currently booted partition
93        let booted = self.pt.booted_partition()?;
94        let next = if let Some(booted) = booted {
95            if booted.partition_type() == crate::partitions::PartitionType::App(next) {
96                AppPartitionSubType::from_ota_app_number(
97                    (current.ota_app_number() + 2) % self.ota_count as u8,
98                )?
99            } else {
100                next
101            }
102        } else {
103            next
104        };
105
106        Ok(next)
107    }
108
109    /// Returns the currently selected app partition.
110    pub fn selected_partition(&mut self) -> Result<crate::partitions::AppPartitionSubType, Error> {
111        self.ota_data()?.current_app_partition()
112    }
113
114    /// Get the [OtaImageState] of the currently selected partition.
115    ///
116    /// # Errors
117    /// A [Error::InvalidState] if no partition is currently selected.
118    pub fn current_ota_state(&mut self) -> Result<OtaImageState, Error> {
119        self.ota_data()?.current_ota_state()
120    }
121
122    /// Set the [OtaImageState] of the currently selected slot.
123    ///
124    /// # Errors
125    /// A [Error::InvalidState] if no partition is currently selected.
126    pub fn set_current_ota_state(&mut self, state: OtaImageState) -> Result<(), Error> {
127        self.ota_data()?.set_current_ota_state(state)
128    }
129
130    /// Selects the next active OTA-slot as current.
131    ///
132    /// After calling this other functions referencing the current partition will use the newly
133    /// activated partition.
134    pub fn activate_next_partition(&mut self) -> Result<(), Error> {
135        let next_slot = self.next_ota_part()?;
136        self.ota_data()?.set_current_app_partition(next_slot)
137    }
138
139    /// Returns a [FlashRegion] along with the [AppPartitionSubType] for the
140    /// partition which would be selected by [Self::activate_next_partition].
141    pub fn next_partition(&mut self) -> Result<(FlashRegion<'_, 'd>, AppPartitionSubType), Error> {
142        let next_slot = self.next_ota_part()?;
143
144        let flash_region = self
145            .pt
146            .find_partition(crate::partitions::PartitionType::App(next_slot))?
147            .ok_or(Error::Invalid)?
148            .as_flash_region(self.flash);
149
150        Ok((flash_region, next_slot))
151    }
152
153    /// Reset the OTA-data.
154    ///
155    /// If present this will activate the FACTORY image, OTA0 otherwise.
156    pub fn reset_data(&mut self) -> Result<(), Error> {
157        // `Factory` resets the OTA data - the bootloader will
158        // check if a FACTORY image is present and boot it,
159        // will choose OTA0 otherwise
160        self.ota_data()?
161            .set_current_app_partition(AppPartitionSubType::Factory)
162    }
163}