fork
Dependencies: BLE_API mbed-dev-bin nRF51822
Fork of microbit-dal by
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 }
Generated on Tue Jul 12 2022 19:47:35 by 1.7.2