Skip to content

File PersistentStorage.h

File List > external-docs > libDaisy > src > util > PersistentStorage.h

Go to the documentation of this file

Source Code

#pragma once

#include "daisy_core.h"
#include "per/qspi.h"
#include "sys/dma.h"

namespace daisy
{
template <typename SettingStruct>
class PersistentStorage
{
  public:
    enum class State
    {
        UNKNOWN = 0,
        FACTORY = 1,
        USER    = 2,
    };

    PersistentStorage(QSPIHandle &qspi)
    : qspi_(qspi),
      address_offset_(0),
      default_settings_(),
      settings_(),
      state_(State::UNKNOWN)
    {
    }

    void Init(const SettingStruct &defaults, uint32_t address_offset = 0)
    {
        default_settings_ = defaults;
        settings_         = defaults;
        address_offset_   = address_offset & (uint32_t)(~0xff);
        auto storage_data
            = reinterpret_cast<SaveStruct *>(qspi_.GetData(address_offset_));

        // check to see if the state is already in use.
        State cur_state = storage_data->storage_state;
        if(cur_state != State::FACTORY && cur_state != State::USER)
        {
            // Initialize the Data store State::FACTORY, and the DefaultSettings
            state_ = State::FACTORY;
            StoreSettingsIfChanged();
        }
        else
        {
            state_    = cur_state;
            settings_ = storage_data->user_data;
        }
    }

    State GetState() const { return state_; }

    SettingStruct &GetSettings() { return settings_; }

    void Save()
    {
        state_ = State::USER;
        StoreSettingsIfChanged();
    }

    void RestoreDefaults()
    {
        settings_ = default_settings_;
        state_    = State::FACTORY;
        StoreSettingsIfChanged();
    }

  private:
    struct SaveStruct
    {
        State         storage_state;
        SettingStruct user_data;
    };

    void StoreSettingsIfChanged()
    {
        SaveStruct s;
        s.storage_state = state_;
        s.user_data     = settings_;

        void *data_ptr = qspi_.GetData(address_offset_);

#if !UNIT_TEST
        // Caching behavior is different when running programs outside internal flash
        // so we need to explicitly invalidate the QSPI mapped memory to ensure we are
        // comparing the local settings with the most recently persisted settings.
        if(System::GetProgramMemoryRegion()
           != System::MemoryRegion::INTERNAL_FLASH)
        {
            dsy_dma_invalidate_cache_for_buffer((uint8_t *)data_ptr, sizeof(s));
        }
#endif

        // Only actually save if the new data is different
        // Use the `==operator` in custom SettingStruct to fine tune
        // what may or may not trigger the erase/save.
        auto storage_data = reinterpret_cast<SaveStruct *>(data_ptr);
        if(settings_ != storage_data->user_data)
        {
            qspi_.Erase(address_offset_, address_offset_ + sizeof(s));
            qspi_.Write(address_offset_, sizeof(s), (uint8_t *)&s);
        }
    }

    QSPIHandle &  qspi_;
    uint32_t      address_offset_;
    SettingStruct default_settings_;
    SettingStruct settings_;
    State         state_;
};

} // namespace daisy