Attempting to publish a tree

Dependencies:   BLE_API mbed-dev-bin nRF51822

Fork of microbit-dal by Lancaster University

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MicroBitStorage.cpp Source File

MicroBitStorage.cpp

00001 /*
00002 The MIT License (MIT)
00003 
00004 Copyright (c) 2016 British Broadcasting Corporation.
00005 This software is provided by Lancaster University by arrangement with the BBC.
00006 
00007 Permission is hereby granted, free of charge, to any person obtaining a
00008 copy of this software and associated documentation files (the "Software"),
00009 to deal in the Software without restriction, including without limitation
00010 the rights to use, copy, modify, merge, publish, distribute, sublicense,
00011 and/or sell copies of the Software, and to permit persons to whom the
00012 Software is furnished to do so, subject to the following conditions:
00013 
00014 The above copyright notice and this permission notice shall be included in
00015 all copies or substantial portions of the Software.
00016 
00017 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00018 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00019 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
00020 THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00021 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
00022 FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
00023 DEALINGS IN THE SOFTWARE.
00024 */
00025 
00026 /**
00027   * Class definition for the MicroBitStorage class.
00028   * This allows reading and writing of FLASH memory.
00029   */
00030 
00031 #include "MicroBitConfig.h"
00032 #include "MicroBitStorage.h"
00033 #include "MicroBitCompat.h"
00034 
00035 /**
00036   * Default constructor.
00037   *
00038   * Creates an instance of MicroBitStorage which acts like a KeyValueStore
00039   * that allows the retrieval, addition and deletion of KeyValuePairs.
00040   */
00041 MicroBitStorage::MicroBitStorage()
00042 {
00043     //initialise our magic block, if required.
00044     size();
00045 }
00046 
00047 /**
00048   * Writes the given number of bytes to the address specified.
00049   *
00050   * @param buffer the data to write.
00051   *
00052   * @param address the location in memory to write to.
00053   *
00054   * @param length the number of bytes to write.
00055   *
00056   * @note currently not implemented.
00057   */
00058 int MicroBitStorage::writeBytes(uint8_t *buffer, uint32_t address, int length)
00059 {
00060     (void) buffer;
00061     (void) address;
00062     (void) length;
00063 
00064     return MICROBIT_OK;
00065 }
00066 
00067 /**
00068   * Method for erasing a page in flash.
00069   *
00070   * @param page_address Address of the first word in the page to be erased.
00071   */
00072 void MicroBitStorage::flashPageErase(uint32_t * page_address)
00073 {
00074     // Turn on flash erase enable and wait until the NVMC is ready:
00075     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Een << NVMC_CONFIG_WEN_Pos);
00076 
00077     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00078 
00079     // Erase page:
00080     NRF_NVMC->ERASEPAGE = (uint32_t)page_address;
00081 
00082     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00083 
00084     // Turn off flash erase enable and wait until the NVMC is ready:
00085     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
00086 
00087     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00088 }
00089 
00090 /**
00091   * Function for copying words from one location to another.
00092   *
00093   * @param from the address to copy data from.
00094   *
00095   * @param to the address to copy the data to.
00096   *
00097   * @param sizeInWords the number of words to copy
00098   */
00099 void MicroBitStorage::flashCopy(uint32_t* from, uint32_t* to, int sizeInWords)
00100 {
00101     // Turn on flash write enable and wait until the NVMC is ready:
00102     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
00103 
00104     while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
00105 
00106     for(int i = 0; i < sizeInWords; i++)
00107     {
00108         *(to + i) = *(from + i);
00109         while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
00110     }
00111 
00112     // Turn off flash write enable and wait until the NVMC is ready:
00113     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
00114     while (NRF_NVMC->READY == NVMC_READY_READY_Busy) {};
00115 }
00116 
00117 /**
00118   * Method for writing a word of data in flash with a value.
00119   *
00120   * @param address Address of the word to change.
00121   *
00122   * @param value Value to be written to flash.
00123   */
00124 void MicroBitStorage::flashWordWrite(uint32_t * address, uint32_t value)
00125 {
00126     // Turn on flash write enable and wait until the NVMC is ready:
00127     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Wen << NVMC_CONFIG_WEN_Pos);
00128 
00129     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00130 
00131     *address = value;
00132 
00133     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00134 
00135     // Turn off flash write enable and wait until the NVMC is ready:
00136     NRF_NVMC->CONFIG = (NVMC_CONFIG_WEN_Ren << NVMC_CONFIG_WEN_Pos);
00137 
00138     while (NRF_NVMC->READY == NVMC_READY_READY_Busy);
00139 }
00140 
00141 /**
00142   * Function for populating the scratch page with a KeyValueStore.
00143   *
00144   * @param store the KeyValueStore struct to write to the scratch page.
00145   */
00146 void MicroBitStorage::scratchKeyValueStore(KeyValueStore store)
00147 {
00148     //calculate our various offsets
00149     uint32_t *s = (uint32_t *) &store;
00150     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00151 
00152     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00153 
00154     //KeyValueStore is word aligned.
00155     int wordsToWrite = sizeof(KeyValueStore) / 4;
00156 
00157     //write the given KeyValueStore
00158     for (int i = 0; i < wordsToWrite; i++)
00159     {
00160         flashWordWrite(scratchPointer, *s);
00161         scratchPointer++;
00162         s++;
00163     }
00164 }
00165 
00166 /**
00167   * Function for populating the scratch page with a KeyValuePair.
00168   *
00169   * @param pair the KeyValuePair struct to write to the scratch page.
00170   *
00171   * @param flashPointer the pointer in flash where this KeyValuePair resides. This pointer
00172   * is used to determine the offset into the scratch page, where the KeyValuePair should
00173   * be written.
00174   */
00175 void MicroBitStorage::scratchKeyValuePair(KeyValuePair pair, uint32_t* flashPointer)
00176 {
00177     //we can only write using words
00178     uint32_t *p = (uint32_t *) &pair;
00179 
00180     //calculate our various offsets
00181     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00182     uint32_t pg_num  = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
00183 
00184     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00185     uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
00186 
00187     uint32_t flashPointerOffset = flashPointer - flashBlockPointer;
00188 
00189     scratchPointer += flashPointerOffset;
00190 
00191     //KeyValuePair is word aligned...
00192     int wordsToWrite = sizeof(KeyValuePair) / 4;
00193 
00194     //write
00195     for (int i = 0; i < wordsToWrite; i++)
00196     {
00197         flashWordWrite(scratchPointer, *p);
00198         scratchPointer++;
00199         p++;
00200     }
00201 }
00202 
00203 /**
00204   * Places a given key, and it's corresponding value into flash at the earliest
00205   * available point.
00206   *
00207   * @param key the unique name that should be used as an identifier for the given data.
00208   *            The key is presumed to be null terminated.
00209   *
00210   * @param data a pointer to the beginning of the data to be persisted.
00211   *
00212   * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
00213   */
00214 int MicroBitStorage::put(const char *key, uint8_t *data)
00215 {
00216     KeyValuePair pair = KeyValuePair();
00217 
00218     memcpy(pair.key, key, min(sizeof(pair.key), strlen(key)));
00219     memcpy(pair.value, data, sizeof(pair.value));
00220 
00221     //calculate our various offsets.
00222     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00223     uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
00224     uint32_t *flashBlockPointer = flashPointer;
00225     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00226 
00227     uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
00228     uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
00229 
00230     int storeSize = size();
00231 
00232     //our KeyValueStore struct is always at 0
00233     flashPointer += kvStoreSize;
00234 
00235     KeyValuePair storedPair = KeyValuePair();
00236 
00237     int found = 0;
00238 
00239     //erase our scratch page
00240     flashPageErase(scratchPointer);
00241 
00242     //iterate through key value pairs in flash, writing them to the scratch page.
00243     for(int i = 0; i < storeSize; i++)
00244     {
00245         memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
00246 
00247         //check if the keys match...
00248         if(strcmp((char *)storedPair.key, (char *)pair.key) == 0)
00249         {
00250             found = 1;
00251             //scratch our KeyValueStore struct so that it is preserved.
00252             scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00253             scratchKeyValuePair(pair, flashPointer);
00254         }
00255         else
00256         {
00257             scratchKeyValuePair(storedPair, flashPointer);
00258         }
00259 
00260         flashPointer += kvPairSize;
00261     }
00262 
00263     if(!found)
00264     {
00265         //if we haven't got a match for the key, check we can add a new KeyValuePair
00266         if(storeSize == (int)((pg_size - kvStoreSize) / MICROBIT_STORAGE_BLOCK_SIZE))
00267             return MICROBIT_NO_RESOURCES;
00268 
00269         storeSize += 1;
00270 
00271         //scratch our updated values.
00272         scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00273         scratchKeyValuePair(pair, flashPointer);
00274     }
00275 
00276     //erase our storage page
00277     flashPageErase((uint32_t *)flashBlockPointer);
00278 
00279     //copy from scratch to storage.
00280     flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
00281 
00282     return MICROBIT_OK;
00283 }
00284 
00285 /**
00286   * Places a given key, and it's corresponding value into flash at the earliest
00287   * available point.
00288   *
00289   * @param key the unique name that should be used as an identifier for the given data.
00290   *
00291   * @param data a pointer to the beginning of the data to be persisted.
00292   *
00293   * @return MICROBIT_OK on success, or MICROBIT_NO_RESOURCES if the storage page is full
00294   */
00295 int MicroBitStorage::put(ManagedString key, uint8_t* data)
00296 {
00297     return put((char *)key.toCharArray(), data);
00298 }
00299 
00300 /**
00301   * Retreives a KeyValuePair identified by a given key.
00302   *
00303   * @param key the unique name used to identify a KeyValuePair in flash.
00304   *
00305   * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
00306   *         NULL if the key was not found in storage.
00307   *
00308   * @note it is up to the user to free memory after use.
00309   */
00310 KeyValuePair* MicroBitStorage::get(const char* key)
00311 {
00312     //calculate our offsets for our storage page
00313     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00314     uint32_t pg_num  = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
00315 
00316     uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
00317 
00318     int storeSize = size();
00319 
00320     //we haven't got anything stored, so return...
00321     if(storeSize == 0)
00322         return NULL;
00323 
00324     //our KeyValueStore struct is always at 0
00325     flashBlockPointer += sizeof(KeyValueStore) / 4;
00326 
00327     KeyValuePair *pair = new KeyValuePair();
00328 
00329     int i;
00330 
00331     //iterate through flash until we have a match, or drop out.
00332     for(i = 0; i < storeSize; i++)
00333     {
00334         memcpy(pair, flashBlockPointer, sizeof(KeyValuePair));
00335 
00336         if(strcmp(key,(char *)pair->key) == 0)
00337             break;
00338 
00339         flashBlockPointer += sizeof(KeyValuePair) / 4;
00340     }
00341 
00342     //clean up
00343     if(i == storeSize)
00344     {
00345         delete pair;
00346         return NULL;
00347     }
00348 
00349     return pair;
00350 }
00351 
00352 /**
00353   * Retreives a KeyValuePair identified by a given key.
00354   *
00355   * @param key the unique name used to identify a KeyValuePair in flash.
00356   *
00357   * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
00358   *         NULL if the key was not found in storage.
00359   *
00360   * @note it is up to the user to free memory after use.
00361   */
00362 KeyValuePair* MicroBitStorage::get(ManagedString key)
00363 {
00364     return get((char *)key.toCharArray());
00365 }
00366 
00367 /**
00368   * Removes a KeyValuePair identified by a given key.
00369   *
00370   * @param key the unique name used to identify a KeyValuePair in flash.
00371   *
00372   * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
00373   *         was not found in flash.
00374   */
00375 int MicroBitStorage::remove(const char* key)
00376 {
00377     //calculate our various offsets
00378     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00379     uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
00380     uint32_t *flashBlockPointer = flashPointer;
00381     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00382 
00383     uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
00384     uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
00385 
00386     int storeSize = size();
00387 
00388     //if we have no data, we have nothing to do.
00389     if(storeSize == 0)
00390         return MICROBIT_NO_DATA;
00391 
00392     //our KeyValueStore struct is always at 0
00393     flashPointer += kvStoreSize;
00394     scratchPointer += kvStoreSize;
00395 
00396     KeyValuePair storedPair = KeyValuePair();
00397 
00398     int found = 0;
00399 
00400     //set up our scratch area
00401     flashPageErase(scratchPointer);
00402 
00403     //iterate through our flash copy pairs to scratch, unless there is a key patch
00404     for(int i = 0; i < storeSize; i++)
00405     {
00406         memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
00407 
00408         //if we have a match, don't increment our scratchPointer
00409         if(strcmp((char *)storedPair.key, (char *)key) == 0)
00410         {
00411             found = 1;
00412             //write our new KeyValueStore data
00413             scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize - 1));
00414         }
00415         else
00416         {
00417             //otherwise copy the KeyValuePair from our storage page.
00418             flashCopy(flashPointer, scratchPointer, sizeof(KeyValuePair) / 4);
00419             scratchPointer += sizeof(KeyValuePair) / 4;
00420         }
00421 
00422         flashPointer += sizeof(KeyValuePair) / 4;
00423     }
00424 
00425     //if we haven't got a match, write our old KeyValueStore struct
00426     if(!found)
00427     {
00428         scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00429         return MICROBIT_NO_DATA;
00430     }
00431 
00432     //copy scratch to our storage page
00433     flashPageErase((uint32_t *)flashBlockPointer);
00434     flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
00435 
00436     return MICROBIT_OK;
00437 }
00438 
00439 /**
00440   * Removes a KeyValuePair identified by a given key.
00441   *
00442   * @param key the unique name used to identify a KeyValuePair in flash.
00443   *
00444   * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
00445   *         was not found in flash.
00446   */
00447 int MicroBitStorage::remove(ManagedString key)
00448 {
00449     return remove((char *)key.toCharArray());
00450 }
00451 
00452 /**
00453   * The size of the flash based KeyValueStore.
00454   *
00455   * @return the number of entries in the key value store
00456   */
00457 int MicroBitStorage::size()
00458 {
00459     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00460     uint32_t pg_num  = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
00461 
00462     uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
00463 
00464     KeyValueStore store = KeyValueStore();
00465 
00466     //read our data!
00467     memcpy(&store, flashBlockPointer, sizeof(KeyValueStore));
00468 
00469     //if we haven't used flash before, we need to configure it
00470     if(store.magic != MICROBIT_STORAGE_MAGIC)
00471     {
00472         store.magic = MICROBIT_STORAGE_MAGIC;
00473         store.size = 0;
00474 
00475         //erase the scratch page and write our new KeyValueStore
00476         flashPageErase((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)));
00477         scratchKeyValueStore(store);
00478 
00479         //erase flash, and copy the scratch page over
00480         flashPageErase((uint32_t *)flashBlockPointer);
00481         flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, pg_size/4);
00482     }
00483 
00484     return store.size;
00485 }