mbed.org local branch of microbit-dal. The real version lives in git at https://github.com/lancaster-university/microbit-dal

Dependencies:   BLE_API nRF51822 mbed-dev-bin

Dependents:   microbit Microbit IoTChallenge1 microbit ... more

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   * @param dataSize the size of the data to be persisted
00213   *
00214   * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the key or size is too large,
00215   *         MICROBIT_NO_RESOURCES if the storage page is full
00216   */
00217 int MicroBitStorage::put(const char *key, uint8_t *data, int dataSize)
00218 {
00219     KeyValuePair pair = KeyValuePair();
00220 
00221     int keySize = strlen(key) + 1;
00222 
00223     if(keySize > (int)sizeof(pair.key) || dataSize > (int)sizeof(pair.value) || dataSize < 0)
00224         return MICROBIT_INVALID_PARAMETER;
00225 
00226     KeyValuePair *currentValue = get(key);
00227 
00228     int upToDate = currentValue && (memcmp(currentValue->value, data, dataSize) == 0);
00229 
00230     if(currentValue)
00231         delete currentValue;
00232 
00233     if(upToDate)
00234         return MICROBIT_OK;
00235 
00236     memcpy(pair.key, key, keySize);
00237     memcpy(pair.value, data, dataSize);
00238 
00239     //calculate our various offsets.
00240     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00241     uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
00242     uint32_t *flashBlockPointer = flashPointer;
00243     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00244 
00245     uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
00246     uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
00247 
00248     int storeSize = size();
00249 
00250     //our KeyValueStore struct is always at 0
00251     flashPointer += kvStoreSize;
00252 
00253     KeyValuePair storedPair = KeyValuePair();
00254 
00255     int found = 0;
00256 
00257     //erase our scratch page
00258     flashPageErase(scratchPointer);
00259 
00260     //iterate through key value pairs in flash, writing them to the scratch page.
00261     for(int i = 0; i < storeSize; i++)
00262     {
00263         memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
00264 
00265         //check if the keys match...
00266         if(strcmp((char *)storedPair.key, (char *)pair.key) == 0)
00267         {
00268             found = 1;
00269             //scratch our KeyValueStore struct so that it is preserved.
00270             scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00271             scratchKeyValuePair(pair, flashPointer);
00272         }
00273         else
00274         {
00275             scratchKeyValuePair(storedPair, flashPointer);
00276         }
00277 
00278         flashPointer += kvPairSize;
00279     }
00280 
00281     if(!found)
00282     {
00283         //if we haven't got a match for the key, check we can add a new KeyValuePair
00284         if(storeSize == (int)((pg_size - kvStoreSize) / MICROBIT_STORAGE_BLOCK_SIZE))
00285             return MICROBIT_NO_RESOURCES;
00286 
00287         storeSize += 1;
00288 
00289         //scratch our updated values.
00290         scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00291         scratchKeyValuePair(pair, flashPointer);
00292     }
00293 
00294     //erase our storage page
00295     flashPageErase((uint32_t *)flashBlockPointer);
00296 
00297     //copy from scratch to storage.
00298     flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
00299 
00300     return MICROBIT_OK;
00301 }
00302 
00303 /**
00304   * Places a given key, and it's corresponding value into flash at the earliest
00305   * available point.
00306   *
00307   * @param key the unique name that should be used as an identifier for the given data.
00308   *
00309   * @param data a pointer to the beginning of the data to be persisted.
00310   *
00311   * @param dataSize the size of the data to be persisted
00312   *
00313   * @return MICROBIT_OK on success, MICROBIT_INVALID_PARAMETER if the key or size is too large,
00314   *         MICROBIT_NO_RESOURCES if the storage page is full
00315   */
00316 int MicroBitStorage::put(ManagedString key, uint8_t* data, int dataSize)
00317 {
00318     return put((char *)key.toCharArray(), data, dataSize);
00319 }
00320 
00321 /**
00322   * Retreives a KeyValuePair identified by a given key.
00323   *
00324   * @param key the unique name used to identify a KeyValuePair in flash.
00325   *
00326   * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
00327   *         NULL if the key was not found in storage.
00328   *
00329   * @note it is up to the user to free memory after use.
00330   */
00331 KeyValuePair* MicroBitStorage::get(const char* key)
00332 {
00333     //calculate our offsets for our storage page
00334     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00335     uint32_t pg_num  = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
00336 
00337     uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
00338 
00339     int storeSize = size();
00340 
00341     //we haven't got anything stored, so return...
00342     if(storeSize == 0)
00343         return NULL;
00344 
00345     //our KeyValueStore struct is always at 0
00346     flashBlockPointer += sizeof(KeyValueStore) / 4;
00347 
00348     KeyValuePair *pair = new KeyValuePair();
00349 
00350     int i;
00351 
00352     //iterate through flash until we have a match, or drop out.
00353     for(i = 0; i < storeSize; i++)
00354     {
00355         memcpy(pair, flashBlockPointer, sizeof(KeyValuePair));
00356 
00357         if(strcmp(key,(char *)pair->key) == 0)
00358             break;
00359 
00360         flashBlockPointer += sizeof(KeyValuePair) / 4;
00361     }
00362 
00363     //clean up
00364     if(i == storeSize)
00365     {
00366         delete pair;
00367         return NULL;
00368     }
00369 
00370     return pair;
00371 }
00372 
00373 /**
00374   * Retreives a KeyValuePair identified by a given key.
00375   *
00376   * @param key the unique name used to identify a KeyValuePair in flash.
00377   *
00378   * @return a pointer to a heap allocated KeyValuePair struct, this pointer will be
00379   *         NULL if the key was not found in storage.
00380   *
00381   * @note it is up to the user to free memory after use.
00382   */
00383 KeyValuePair* MicroBitStorage::get(ManagedString key)
00384 {
00385     return get((char *)key.toCharArray());
00386 }
00387 
00388 /**
00389   * Removes a KeyValuePair identified by a given key.
00390   *
00391   * @param key the unique name used to identify a KeyValuePair in flash.
00392   *
00393   * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
00394   *         was not found in flash.
00395   */
00396 int MicroBitStorage::remove(const char* key)
00397 {
00398     //calculate our various offsets
00399     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00400     uint32_t *flashPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET));
00401     uint32_t *flashBlockPointer = flashPointer;
00402     uint32_t *scratchPointer = (uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET));
00403 
00404     uint32_t kvStoreSize = sizeof(KeyValueStore) / 4;
00405     uint32_t kvPairSize = sizeof(KeyValuePair) / 4;
00406 
00407     int storeSize = size();
00408 
00409     //if we have no data, we have nothing to do.
00410     if(storeSize == 0)
00411         return MICROBIT_NO_DATA;
00412 
00413     //our KeyValueStore struct is always at 0
00414     flashPointer += kvStoreSize;
00415     scratchPointer += kvStoreSize;
00416 
00417     KeyValuePair storedPair = KeyValuePair();
00418 
00419     int found = 0;
00420 
00421     //set up our scratch area
00422     flashPageErase(scratchPointer);
00423 
00424     //iterate through our flash copy pairs to scratch, unless there is a key patch
00425     for(int i = 0; i < storeSize; i++)
00426     {
00427         memcpy(&storedPair, flashPointer, sizeof(KeyValuePair));
00428 
00429         //if we have a match, don't increment our scratchPointer
00430         if(strcmp((char *)storedPair.key, (char *)key) == 0)
00431         {
00432             found = 1;
00433             //write our new KeyValueStore data
00434             scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize - 1));
00435         }
00436         else
00437         {
00438             //otherwise copy the KeyValuePair from our storage page.
00439             flashCopy(flashPointer, scratchPointer, sizeof(KeyValuePair) / 4);
00440             scratchPointer += sizeof(KeyValuePair) / 4;
00441         }
00442 
00443         flashPointer += sizeof(KeyValuePair) / 4;
00444     }
00445 
00446     //if we haven't got a match, write our old KeyValueStore struct
00447     if(!found)
00448     {
00449         scratchKeyValueStore(KeyValueStore(MICROBIT_STORAGE_MAGIC, storeSize));
00450         return MICROBIT_NO_DATA;
00451     }
00452 
00453     //copy scratch to our storage page
00454     flashPageErase((uint32_t *)flashBlockPointer);
00455     flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, kvStoreSize + (storeSize * kvPairSize));
00456 
00457     return MICROBIT_OK;
00458 }
00459 
00460 /**
00461   * Removes a KeyValuePair identified by a given key.
00462   *
00463   * @param key the unique name used to identify a KeyValuePair in flash.
00464   *
00465   * @return MICROBIT_OK on success, or MICROBIT_NO_DATA if the given key
00466   *         was not found in flash.
00467   */
00468 int MicroBitStorage::remove(ManagedString key)
00469 {
00470     return remove((char *)key.toCharArray());
00471 }
00472 
00473 /**
00474   * The size of the flash based KeyValueStore.
00475   *
00476   * @return the number of entries in the key value store
00477   */
00478 int MicroBitStorage::size()
00479 {
00480     uint32_t pg_size = NRF_FICR->CODEPAGESIZE;
00481     uint32_t pg_num  = NRF_FICR->CODESIZE - MICROBIT_STORAGE_STORE_PAGE_OFFSET;
00482 
00483     uint32_t *flashBlockPointer = (uint32_t *)(pg_size * pg_num);
00484 
00485     KeyValueStore store = KeyValueStore();
00486 
00487     //read our data!
00488     memcpy(&store, flashBlockPointer, sizeof(KeyValueStore));
00489 
00490     //if we haven't used flash before, we need to configure it
00491     if(store.magic != MICROBIT_STORAGE_MAGIC)
00492     {
00493         store.magic = MICROBIT_STORAGE_MAGIC;
00494         store.size = 0;
00495 
00496         //erase the scratch page and write our new KeyValueStore
00497         flashPageErase((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)));
00498         scratchKeyValueStore(store);
00499 
00500         //erase flash, and copy the scratch page over
00501         flashPageErase((uint32_t *)flashBlockPointer);
00502         flashCopy((uint32_t *)(pg_size * (NRF_FICR->CODESIZE - MICROBIT_STORAGE_SCRATCH_PAGE_OFFSET)), flashBlockPointer, pg_size/4);
00503     }
00504 
00505     return store.size;
00506 }