fsdfds
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 * @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 }
Generated on Wed Jul 13 2022 00:58:03 by 1.7.2