blob: 243d7d50531ceee98e8748bc211473308cb703d8 [file] [log] [blame]
/*
* Copyright (c) 2020, The OpenThread Authors.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the copyright holder nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "flash.hpp"
#if OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE
#include <stdio.h>
#include <openthread/platform/flash.h>
#include "common/code_utils.hpp"
#include "common/instance.hpp"
namespace ot {
const uint32_t ot::Flash::sSwapActive;
const uint32_t ot::Flash::sSwapInactive;
void Flash::Init(void)
{
RecordHeader record;
otPlatFlashInit(&GetInstance());
mSwapSize = otPlatFlashGetSwapSize(&GetInstance());
for (mSwapIndex = 0;; mSwapIndex++)
{
uint32_t swapMarker;
if (mSwapIndex >= 2)
{
Wipe();
ExitNow();
}
otPlatFlashRead(&GetInstance(), mSwapIndex, 0, &swapMarker, sizeof(swapMarker));
if (swapMarker == sSwapActive)
{
break;
}
}
for (mSwapUsed = kSwapMarkerSize; mSwapUsed <= mSwapSize - sizeof(record); mSwapUsed += record.GetSize())
{
otPlatFlashRead(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(record));
if (!record.IsAddBeginSet())
{
break;
}
if (!record.IsAddCompleteSet())
{
break;
}
}
SanitizeFreeSpace();
exit:
return;
}
void Flash::SanitizeFreeSpace(void)
{
uint32_t temp;
bool sanitizeNeeded = false;
if (mSwapUsed & 3)
{
ExitNow(sanitizeNeeded = true);
}
for (uint32_t offset = mSwapUsed; offset < mSwapSize; offset += sizeof(temp))
{
otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &temp, sizeof(temp));
if (temp != ~0U)
{
ExitNow(sanitizeNeeded = true);
}
}
exit:
if (sanitizeNeeded)
{
Swap();
}
}
Error Flash::Get(uint16_t aKey, int aIndex, uint8_t *aValue, uint16_t *aValueLength) const
{
Error error = kErrorNotFound;
uint16_t valueLength = 0;
int index = 0; // This must be initialized to 0. See [Note] in Delete().
uint32_t offset;
RecordHeader record;
for (offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
{
otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
if ((record.GetKey() != aKey) || !record.IsValid())
{
continue;
}
if (record.IsFirst())
{
index = 0;
}
if (index == aIndex)
{
if (aValue && aValueLength)
{
uint16_t readLength = *aValueLength;
if (readLength > record.GetLength())
{
readLength = record.GetLength();
}
otPlatFlashRead(&GetInstance(), mSwapIndex, offset + sizeof(record), aValue, readLength);
}
valueLength = record.GetLength();
error = kErrorNone;
}
index++;
}
if (aValueLength)
{
*aValueLength = valueLength;
}
return error;
}
Error Flash::Set(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
{
return Add(aKey, true, aValue, aValueLength);
}
Error Flash::Add(uint16_t aKey, const uint8_t *aValue, uint16_t aValueLength)
{
bool first = (Get(aKey, 0, nullptr, nullptr) == kErrorNotFound);
return Add(aKey, first, aValue, aValueLength);
}
Error Flash::Add(uint16_t aKey, bool aFirst, const uint8_t *aValue, uint16_t aValueLength)
{
Error error = kErrorNone;
Record record;
record.Init(aKey, aFirst);
record.SetData(aValue, aValueLength);
OT_ASSERT((mSwapSize - record.GetSize()) >= kSwapMarkerSize);
if ((mSwapSize - record.GetSize()) < mSwapUsed)
{
Swap();
VerifyOrExit((mSwapSize - record.GetSize()) >= mSwapUsed, error = kErrorNoBufs);
}
otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, record.GetSize());
record.SetAddCompleteFlag();
otPlatFlashWrite(&GetInstance(), mSwapIndex, mSwapUsed, &record, sizeof(RecordHeader));
mSwapUsed += record.GetSize();
exit:
return error;
}
bool Flash::DoesValidRecordExist(uint32_t aOffset, uint16_t aKey) const
{
RecordHeader record;
bool rval = false;
for (; aOffset < mSwapUsed; aOffset += record.GetSize())
{
otPlatFlashRead(&GetInstance(), mSwapIndex, aOffset, &record, sizeof(record));
if (record.IsValid() && record.IsFirst() && (record.GetKey() == aKey))
{
ExitNow(rval = true);
}
}
exit:
return rval;
}
void Flash::Swap(void)
{
uint8_t dstIndex = !mSwapIndex;
uint32_t dstOffset = kSwapMarkerSize;
Record record;
otPlatFlashErase(&GetInstance(), dstIndex);
for (uint32_t srcOffset = kSwapMarkerSize; srcOffset < mSwapUsed; srcOffset += record.GetSize())
{
otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, sizeof(RecordHeader));
VerifyOrExit(record.IsAddBeginSet());
if (!record.IsValid() || DoesValidRecordExist(srcOffset + record.GetSize(), record.GetKey()))
{
continue;
}
otPlatFlashRead(&GetInstance(), mSwapIndex, srcOffset, &record, record.GetSize());
otPlatFlashWrite(&GetInstance(), dstIndex, dstOffset, &record, record.GetSize());
dstOffset += record.GetSize();
}
exit:
otPlatFlashWrite(&GetInstance(), dstIndex, 0, &sSwapActive, sizeof(sSwapActive));
otPlatFlashWrite(&GetInstance(), mSwapIndex, 0, &sSwapInactive, sizeof(sSwapInactive));
mSwapIndex = dstIndex;
mSwapUsed = dstOffset;
}
Error Flash::Delete(uint16_t aKey, int aIndex)
{
Error error = kErrorNotFound;
int index = 0; // This must be initialized to 0. See [Note] below.
RecordHeader record;
for (uint32_t offset = kSwapMarkerSize; offset < mSwapUsed; offset += record.GetSize())
{
otPlatFlashRead(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
if ((record.GetKey() != aKey) || !record.IsValid())
{
continue;
}
if (record.IsFirst())
{
index = 0;
}
if ((aIndex == index) || (aIndex == -1))
{
record.SetDeleted();
otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
error = kErrorNone;
}
/* [Note] If the operation gets interrupted here and aIndex is 0, the next record (index == 1) will never get
* marked as first. However, this is not actually an issue because all the methods that iterate over the
* settings area initialize the index to 0, without expecting any record to be effectively marked as first. */
if ((index == 1) && (aIndex == 0))
{
record.SetFirst();
otPlatFlashWrite(&GetInstance(), mSwapIndex, offset, &record, sizeof(record));
}
index++;
}
return error;
}
void Flash::Wipe(void)
{
otPlatFlashErase(&GetInstance(), 0);
otPlatFlashWrite(&GetInstance(), 0, 0, &sSwapActive, sizeof(sSwapActive));
mSwapIndex = 0;
mSwapUsed = sizeof(sSwapActive);
}
} // namespace ot
#endif // OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE