Merge pull request #276 from lancaster-university/sd-safe-storage

Sd safe storage
This commit is contained in:
Joe Finney 2017-03-01 17:43:55 +00:00 committed by GitHub
commit 9adafccbf6
7 changed files with 158 additions and 134 deletions

View file

@ -74,6 +74,10 @@ DEALINGS IN THE SOFTWARE.
#define MICROBIT_BLE_EDDYSTONE_ADV_INTERVAL 400
#define MICROBIT_BLE_EDDYSTONE_DEFAULT_POWER 0xF0
// MicroBitComponent status flags
#define MICROBIT_BLE_STATUS_STORE_SYSATTR 0x02
#define MICROBIT_BLE_STATUS_DISCONNECT 0x04
extern const int8_t MICROBIT_BLE_POWER_LEVEL[];
struct BLESysAttribute
@ -214,6 +218,14 @@ class MicroBitBLEManager : MicroBitComponent
* Stops any currently running BLE advertisements
*/
void stopAdvertising();
/**
* A member function used to defer writes to flash, in order to prevent a write collision with
* softdevice.
* @param handle The handle offered by soft device during pairing.
* */
void deferredSysAttrWrite(Gap::Handle_t handle);
#if CONFIG_ENABLED(MICROBIT_BLE_EDDYSTONE_URL)
/**

View file

@ -377,7 +377,7 @@ extern uint32_t __etext;
// Should be <= MBFS_BLOCK_SIZE.
//
#ifndef MBFS_CACHE_SIZE
#define MBFS_CACHE_SIZE 16
#define MBFS_CACHE_SIZE 0
#endif
//

View file

@ -9,19 +9,6 @@ class MicroBitFlash
{
private:
/**
* Write to flash memory, assuming that a write is valid
* (using need_erase).
*
* @param page_address address of memory to write to.
* Must be word aligned.
* @param buffer address to write from, must be word-aligned.
* @param len number of uint32_t words to write.
*/
void flash_burn(uint32_t* page_address, uint32_t* buffer, int len);
/**
* Check if an erase is required to write to a region in flash memory.
* This is determined if, for any byte:
@ -66,6 +53,18 @@ class MicroBitFlash
* @param page_address address of first word of page.
*/
void erase_page(uint32_t* page_address);
/**
* Write to flash memory, assuming that a write is valid
* (using need_erase).
*
* @param page_address address of memory to write to.
* Must be word aligned.
* @param buffer address to write from, must be word-aligned.
* @param len number of uint32_t words to write.
*/
void flash_burn(uint32_t* page_address, uint32_t* buffer, int len);
};
#endif

View file

@ -124,19 +124,6 @@ class MicroBitStorage
*/
MicroBitStorage();
/**
* Writes the given number of bytes to the address specified.
*
* @param buffer the data to write.
*
* @param address the location in memory to write to.
*
* @param length the number of bytes to write.
*
* @note currently not implemented.
*/
int writeBytes(uint8_t *buffer, uint32_t address, int length);
/**
* Method for erasing a page in flash.
*

View file

@ -127,10 +127,11 @@ static void bleDisconnectionCallback(const Gap::DisconnectionCallbackParams_t *r
{
MicroBitEvent(MICROBIT_ID_BLE, MICROBIT_BLE_EVT_DISCONNECTED);
storeSystemAttributes(reason->handle);
if (MicroBitBLEManager::manager)
{
MicroBitBLEManager::manager->advertise();
MicroBitBLEManager::manager->deferredSysAttrWrite(reason->handle);
}
}
/**
@ -228,6 +229,7 @@ MicroBitBLEManager::MicroBitBLEManager(MicroBitStorage &_storage) : storage(&_st
manager = this;
this->ble = NULL;
this->pairingStatus = 0;
this->status = MICROBIT_COMPONENT_RUNNING;
}
/**
@ -268,6 +270,17 @@ void MicroBitBLEManager::advertise()
ble->gap().startAdvertising();
}
/**
* A member function used to defer writes to flash, in order to prevent a write collision with
* softdevice.
* @param handle The handle offered by soft device during pairing.
* */
void MicroBitBLEManager::deferredSysAttrWrite(Gap::Handle_t handle)
{
pairingHandle = handle;
this->status |= MICROBIT_BLE_STATUS_STORE_SYSATTR;
}
/**
* Post constructor initialisation method as the BLE stack cannot be brought
* up in a static context.
@ -483,7 +496,7 @@ void MicroBitBLEManager::pairingComplete(bool success)
if (success)
{
this->pairingStatus |= MICROBIT_BLE_PAIR_SUCCESSFUL;
fiber_add_idle_component(this);
this->status |= MICROBIT_BLE_STATUS_DISCONNECT;
}
}
@ -493,12 +506,20 @@ void MicroBitBLEManager::pairingComplete(bool success)
*/
void MicroBitBLEManager::idleTick()
{
if((system_timer_current_time() - pairing_completed_at_time) >= MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY) {
if (ble)
ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
fiber_remove_idle_component(this);
if (this->status & MICROBIT_BLE_STATUS_DISCONNECT)
{
if((system_timer_current_time() - pairing_completed_at_time) >= MICROBIT_BLE_DISCONNECT_AFTER_PAIRING_DELAY) {
if (ble)
ble->disconnect(pairingHandle, Gap::REMOTE_DEV_TERMINATION_DUE_TO_POWER_OFF);
this->status &= ~MICROBIT_BLE_STATUS_DISCONNECT;
}
}
if (this->status & MICROBIT_BLE_STATUS_STORE_SYSATTR)
{
storeSystemAttributes(pairingHandle);
this->status &= ~MICROBIT_BLE_STATUS_STORE_SYSATTR;
}
}
@ -658,6 +679,8 @@ void MicroBitBLEManager::pairingMode(MicroBitDisplay &display, MicroBitButton &a
// Display our name, visualised as a histogram in the display to aid identification.
showNameHistogram(display);
fiber_add_idle_component(this);
while (1)
{
if (pairingStatus & MICROBIT_BLE_PAIR_REQUEST)

View file

@ -1,17 +1,55 @@
#include "MicroBitConfig.h"
#include "MicroBitFlash.h"
#include "MicroBitDevice.h"
#include "ErrorNo.h"
#include "mbed.h" // NVIC
#define MIN(a,b) ((a)<(b)?(a):(b))
#define WORD_ADDR(x) (((uint32_t)x) & 0xFFFFFFFC)
/*
* The underlying Nordic libraries that support BLE do not compile cleanly with the stringent GCC settings we employ
* If we're compiling under GCC, then we suppress any warnings generated from this code (but not the rest of the DAL)
* The ARM cc compiler is more tolerant. We don't test __GNUC__ here to detect GCC as ARMCC also typically sets this
* as a compatability option, but does not support the options used...
*/
#if !defined(__arm)
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#include "nrf_soc.h"
extern "C" void btle_set_user_evt_handler(void (*func)(uint32_t));
/*
* Return to our predefined compiler settings.
*/
#if !defined(__arm)
#pragma GCC diagnostic pop
#endif
static bool evt_handler_registered = false;
static volatile bool flash_op_complete = false;
static void nvmc_event_handler(uint32_t evt)
{
if(evt == NRF_EVT_FLASH_OPERATION_SUCCESS)
flash_op_complete = true;
}
/**
* Default Constructor
*/
MicroBitFlash::MicroBitFlash()
{
if (!evt_handler_registered)
{
btle_set_user_evt_handler(nvmc_event_handler);
evt_handler_registered = true;
}
}
/**
@ -44,17 +82,33 @@ int MicroBitFlash::need_erase(uint8_t* source, uint8_t* flash_addr, int len)
*/
void MicroBitFlash::erase_page(uint32_t* pg_addr)
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
if (ble_running())
{
flash_op_complete = false;
while(1)
{
if (sd_flash_page_erase(((uint32_t)pg_addr)/PAGE_SIZE) == NRF_SUCCESS)
break;
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)pg_addr;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
wait_ms(10);
}
// Wait for SoftDevice to diable the write operation when it completes...
while(!flash_op_complete);
}
else
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)pg_addr;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) { }
}
}
/**
@ -67,21 +121,40 @@ void MicroBitFlash::erase_page(uint32_t* pg_addr)
* @param len number of uint32_t words to write.
*/
void MicroBitFlash::flash_burn(uint32_t* addr, uint32_t* buffer, int size)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i=0;i<size;i++)
{
if (ble_running())
{
*(addr+i) = *(buffer+i);
// Schedule SoftDevice to write this memory for us, and wait for it to complete.
// This happens ASYNCHRONOUSLY when SD is enabled (and synchronously if disabled!!)
flash_op_complete = false;
while(1)
{
if (sd_flash_write(addr, buffer, size) == NRF_SUCCESS)
break;
wait_ms(10);
}
// Wait for SoftDevice to diable the write operation when it completes...
while(!flash_op_complete);
}
else
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i=0;i<size;i++)
{
*(addr+i) = *(buffer+i);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
/**
@ -104,7 +177,6 @@ void MicroBitFlash::flash_burn(uint32_t* addr, uint32_t* buffer, int size)
int MicroBitFlash::flash_write(void* address, void* from_buffer,
int length, void* scratch_addr)
{
// Ensure that scratch_addr is aligned on a page boundary.
if((uint32_t)scratch_addr & 0x3FF)
return MICROBIT_INVALID_PARAMETER;

View file

@ -30,8 +30,10 @@ DEALINGS IN THE SOFTWARE.
#include "MicroBitConfig.h"
#include "MicroBitStorage.h"
#include "MicroBitFlash.h"
#include "MicroBitCompat.h"
/**
* Default constructor.
*
@ -44,26 +46,6 @@ MicroBitStorage::MicroBitStorage()
size();
}
/**
* Writes the given number of bytes to the address specified.
*
* @param buffer the data to write.
*
* @param address the location in memory to write to.
*
* @param length the number of bytes to write.
*
* @note currently not implemented.
*/
int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
{
(void) buffer;
(void) address;
(void) length;
return MICROBIT_OK;
}
/**
* Method for erasing a page in flash.
*
@ -71,20 +53,8 @@ int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
*/
void MicroBitStorage::flashPageErase(uint32_t * page_address)
{
// Turn on flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Erase page:
NRF_NVMC->ERASEPAGE = (uint32_t)page_address;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Turn off flash erase enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
MicroBitFlash flash;
flash.erase_page(page_address);
}
/**
@ -98,20 +68,8 @@ void MicroBitStorage::flashPageErase(uint32_t * page_address)
*/
void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
for(int i = 0; i < sizeInWords; i++)
{
*(to + i) = *(from + i);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
}
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
MicroBitFlash flash;
flash.flash_burn(to, from, sizeInWords);
}
/**
@ -123,19 +81,7 @@ void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
*/
void MicroBitStorage::flashWordWrite(uint32_t * address, uint32_t value)
{
// Turn on flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
*address = value;
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
// Turn off flash write enable and wait until the NVMC is ready:
NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
flashCopy(&value, address, 1);
}
/**
@ -148,19 +94,10 @@ void MicroBitStorage::scratchKeyValueStore(KeyValueStore store)
//calculate our various offsets
uint32_t *s = (uint32_t *) &store;
uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
//KeyValueStore is word aligned.
int wordsToWrite = sizeof(KeyValueStore) / 4;
//write the given KeyValueStore
for (int i = 0; i < wordsToWrite; i++)
{
flashWordWrite(scratchPointer, *s);
scratchPointer++;
s++;
}
flashCopy(s, scratchPointer, wordsToWrite);
}
/**
@ -191,13 +128,7 @@ void MicroBitStorage::scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPoin
//KeyValuePair is word aligned...
int wordsToWrite = sizeof(KeyValuePair) / 4;
//write
for (int i = 0; i < wordsToWrite; i++)
{
flashWordWrite(scratchPointer, *p);
scratchPointer++;
p++;
}
flashCopy(p, scratchPointer, wordsToWrite);
}
/**