Persistent data storage is a useful tool for Bluetooth low energy applications that need to store exchanged information. For example, when a bonded peer configuration needs to be cached to store the bond identification, GATT Server configuration, and/or GATT Server attribute handles, persistent data storage is used.
The Persistent Storage Manager has a generic API (Persistent Storage Interface) that is used by the SDK modules for common storage access. A generic API means that SDK modules can be reused directly on other platforms. All modules using the Persistent Storage Manager are decoupled from accessing the nRF51822 flash directly, making the SDK modules easier to reuse. For example, the Device Manager can be used on processors other than nRF51822 by implementing a storage module according to the Persistent Storage Manager API for the targeted platform.
The application interface of this module is as defined in the header and should not be altered in order to preserve the other SDK modules. The interface implementation is expected to vary based on the storage solution identified for the system. Therefore, pstorage.h defines the application interface, while pstorage_platform.h is a use case and system specific file that can contain defines and routines needed for a specific implementation of the identified interface. The source file, pstorage.c shall implement the needed APIs. SDK examples include implementation of the interface using the SoftDevice APIs for flash access on the chip.
Multiple SDK modules and the application itself may be required to store data, with each module having its own data size requirements. Therefore, this module allows registering various block size and block count, each with a separate event handler that is notified of the flash access results.
The pstorage_platform.h file contains the information defining where the application data is stored in flash. Generally, the rules are as follows:
nrfjprog –memrd 0x0007D000 –n 8192
nrfjprog –memrd 0x10001014 –n 4
If the start address of the bootloader is 0x7B000, then the swap page start address is at 0x7B000-0x1000=0x7A000 and the two pages of application data are located from 0x7A000-0x2000=0x78000 to 0x79FFF. The memory content of the two application data pages can be read with:
nrfjprog –memrd 0x00078000 –n 8192
Any SDK or application component that is required to store its data registers with the module using the pstorage_register, will at the time of registration request the number of blocks of storage needed. The interface is designed to be asynchronous and the application is expected to register a callback to know the result of a storage access operation. Identified storage access operations include load, store, and clear. Once the application is successfully registered, the application is assigned a handle for all future operations needed for accessing these storage blocks. This handle is abstracted by pstorage_handle_t (in the platform-specific header file).
The application does not have to remember a handle or an identifier for each block. Instead, it needs to remember only one identifier for all the blocks. When a reference to a specific block for flash access is required, pstorage_block_identifier_get API shall be used to get a block specific handle. The base handle and block number starting from 0 are provided as input to the API.
As mentioned earlier, an asynchronous interface is defined for storage access since storing data and clearing data can take time. However, data that is to be stored is not copied by the implementation included in the SDK. Therefore, the data source provided for the store operation using the Store Data API expects resident memory. This means that the memory the data source for this API points to should not be reused or freed unless the client is notified by the asynchronous notification callback registered with the module upon completion of a store operation.
The storage module must be initialized before using any other API of the module.
A module that requires storage must register with the storage module in order to allocate storage blocks for data. The application must register the asynchronous event notification handler, number of blocks, and block size which should be in range of PSTORAGE_MIN_BLOCK_SIZE and PSTORAGE_MAX_BLOCK_SIZE. A reference handle is given to the application once registration is successful and is remembered by the application to reference the storage blocks.
This API provides a specific block reference to the application in allocated blocks. Allocated blocks are identified by the base block identifier provided at the time of registration. Block offset is indexed starting from zero to (number of blocks allocated - 1).
This API shall be called before a load or store operation to a specific block.
This API is used to read data from a storage block. It is permitted to read a part of the block using the offset field. The application should ensure that the destination has enough memory to copy data from the storage block to the destination pointer provided in the API.
This API is used to write data to a storage block. It is permitted to write only a part of the block using the offset field. The application cannot free or reuse the memory that is the source of data until this operation is complete. The event notified using a registered callback will indicate when this operation is complete. The event result indicates whether the operation was successful or not.
This API is used to update data in storage blocks. The application cannot free or reuse the memory that is the source of data until this operation is complete. The event notified using a registered callback will indicate when this operation is complete. The event result indicates whether the operation was successful or not.
This API is used to clear data in storage blocks. The event notified using a registered callback will indicate when this operation is complete. The event result indicates whether the operation was successful or not.
The size requested to be erased has to be equal or a multiple of the block size.
The Persistent Storage Manager uses the pstorage_access_status_get API to communicate to an application how many storage access operations are pending. This is particularly useful when you want to enter power off mode or want to enter a radio intense operation, but before doing so, want to ensure that the storage operations are complete.
Certain use cases require complete control of the entire flash region and do not have typical storage requirements. The storage module then provisions for one application to be registered with it in 'raw' mode. In raw mode, the application is responsible for conceptualizing the flash region as blocks and their management. Dedicated APIs, register, store, and clear are provided to distinguish raw mode from the normal mode. Raw mode APIs have a similar signature to the normal mode.
Because this is not a typical use case, raw mode is included for only a few applications like the DFU, and by default is disabled. It is included only if PSTORAGE_RAW_MODE_ENABLE is defined in the pstorage_platform.h header.
Additional requirements and a few limitations exist when implementing the example included. Some have already been mentioned but the following is a summarized list with more detailed information.
General:
Registration:
Store:
Update:
Clear:
The figure below illustrates the top level state transition diagram of the Persistent Storage Manager.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state when executing a use case which operates only within 1 flash page.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state when executing a use case which operates across multiple pages and only head restore is required.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state when executing use case which operates across 2 pages and both head and tail restore is required.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state when executing a use case which operates across => 3 pages and both the head and tail restore is required.
The figure below illustrates the internal state transition diagram of the DATA ERASE WITH SWAP composite state when executing a use case which operates across => 2 pages and the tail restore is required.