Nordic stack and drivers for the mbed BLE API

Dependents:   BLE_ANCS_SDAPI BLE_temperature BLE_HeartRate writable_gatt ... more

Committer:
Vincent Coubard
Date:
Wed Sep 14 14:39:43 2016 +0100
Revision:
638:c90ae1400bf2
Sync with bdab10dc0f90748b6989c8b577771bb403ca6bd8 from ARMmbed/mbed-os.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
Vincent Coubard 638:c90ae1400bf2 1 /*
Vincent Coubard 638:c90ae1400bf2 2 * Copyright (c) Nordic Semiconductor ASA
Vincent Coubard 638:c90ae1400bf2 3 * All rights reserved.
Vincent Coubard 638:c90ae1400bf2 4 *
Vincent Coubard 638:c90ae1400bf2 5 * Redistribution and use in source and binary forms, with or without modification,
Vincent Coubard 638:c90ae1400bf2 6 * are permitted provided that the following conditions are met:
Vincent Coubard 638:c90ae1400bf2 7 *
Vincent Coubard 638:c90ae1400bf2 8 * 1. Redistributions of source code must retain the above copyright notice, this
Vincent Coubard 638:c90ae1400bf2 9 * list of conditions and the following disclaimer.
Vincent Coubard 638:c90ae1400bf2 10 *
Vincent Coubard 638:c90ae1400bf2 11 * 2. Redistributions in binary form must reproduce the above copyright notice, this
Vincent Coubard 638:c90ae1400bf2 12 * list of conditions and the following disclaimer in the documentation and/or
Vincent Coubard 638:c90ae1400bf2 13 * other materials provided with the distribution.
Vincent Coubard 638:c90ae1400bf2 14 *
Vincent Coubard 638:c90ae1400bf2 15 * 3. Neither the name of Nordic Semiconductor ASA nor the names of other
Vincent Coubard 638:c90ae1400bf2 16 * contributors to this software may be used to endorse or promote products
Vincent Coubard 638:c90ae1400bf2 17 * derived from this software without specific prior written permission.
Vincent Coubard 638:c90ae1400bf2 18 *
Vincent Coubard 638:c90ae1400bf2 19 *
Vincent Coubard 638:c90ae1400bf2 20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
Vincent Coubard 638:c90ae1400bf2 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
Vincent Coubard 638:c90ae1400bf2 22 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
Vincent Coubard 638:c90ae1400bf2 23 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
Vincent Coubard 638:c90ae1400bf2 24 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
Vincent Coubard 638:c90ae1400bf2 25 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
Vincent Coubard 638:c90ae1400bf2 26 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
Vincent Coubard 638:c90ae1400bf2 27 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
Vincent Coubard 638:c90ae1400bf2 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Vincent Coubard 638:c90ae1400bf2 29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Vincent Coubard 638:c90ae1400bf2 30 *
Vincent Coubard 638:c90ae1400bf2 31 */
Vincent Coubard 638:c90ae1400bf2 32
Vincent Coubard 638:c90ae1400bf2 33 #include "fstorage.h"
Vincent Coubard 638:c90ae1400bf2 34 #include <stdio.h>
Vincent Coubard 638:c90ae1400bf2 35 #include <string.h>
Vincent Coubard 638:c90ae1400bf2 36 #include <stdbool.h>
Vincent Coubard 638:c90ae1400bf2 37 #include "fstorage_config.h"
Vincent Coubard 638:c90ae1400bf2 38 #include "nrf_error.h"
Vincent Coubard 638:c90ae1400bf2 39 #include "nrf_soc.h"
Vincent Coubard 638:c90ae1400bf2 40
Vincent Coubard 638:c90ae1400bf2 41
Vincent Coubard 638:c90ae1400bf2 42 #define FS_FLAG_INIT (1 << 0) /**< fstorage has been initialized. */
Vincent Coubard 638:c90ae1400bf2 43 #define FS_FLAG_PROCESSING (1 << 1) /**< fstorage is executing queued flash operations. */
Vincent Coubard 638:c90ae1400bf2 44 #define FS_FLAG_FLASH_REQ_PENDING (1 << 2) /**< fstorage is waiting for a flash operation initiated by another module to complete. */
Vincent Coubard 638:c90ae1400bf2 45
Vincent Coubard 638:c90ae1400bf2 46
Vincent Coubard 638:c90ae1400bf2 47 /**@brief Macro invocation that registers section fs_data.
Vincent Coubard 638:c90ae1400bf2 48 *
Vincent Coubard 638:c90ae1400bf2 49 * @details Required for compilation.
Vincent Coubard 638:c90ae1400bf2 50 */
Vincent Coubard 638:c90ae1400bf2 51 NRF_SECTION_VARS_REGISTER_SECTION(fs_data);
Vincent Coubard 638:c90ae1400bf2 52
Vincent Coubard 638:c90ae1400bf2 53
Vincent Coubard 638:c90ae1400bf2 54 /**@brief Macro invocation that declares symbols used to find the beginning and end of the section fs_data.
Vincent Coubard 638:c90ae1400bf2 55 *
Vincent Coubard 638:c90ae1400bf2 56 * @details Required for compilation.
Vincent Coubard 638:c90ae1400bf2 57 */
Vincent Coubard 638:c90ae1400bf2 58 NRF_SECTION_VARS_REGISTER_SYMBOLS(fs_config_t, fs_data);
Vincent Coubard 638:c90ae1400bf2 59
Vincent Coubard 638:c90ae1400bf2 60
Vincent Coubard 638:c90ae1400bf2 61 /**@defgroup Section vars helper macros.
Vincent Coubard 638:c90ae1400bf2 62 *
Vincent Coubard 638:c90ae1400bf2 63 * @details Macros used to manipulate registered section variables.
Vincent Coubard 638:c90ae1400bf2 64 */
Vincent Coubard 638:c90ae1400bf2 65 /**@brief Get section variable with fstorage configuration by index. */
Vincent Coubard 638:c90ae1400bf2 66 #define FS_SECTION_VARS_GET(i) NRF_SECTION_VARS_GET(i, fs_config_t, fs_data)
Vincent Coubard 638:c90ae1400bf2 67 /**@brief Get the number of registered section variables. */
Vincent Coubard 638:c90ae1400bf2 68 #define FS_SECTION_VARS_COUNT NRF_SECTION_VARS_COUNT(fs_config_t, fs_data)
Vincent Coubard 638:c90ae1400bf2 69 /**@brief Get the start address of the registered section variables. */
Vincent Coubard 638:c90ae1400bf2 70 #define FS_SECTION_VARS_START_ADDR NRF_SECTION_VARS_START_ADDR(fs_data)
Vincent Coubard 638:c90ae1400bf2 71 /**@brief Get the end address of the registered section variables. */
Vincent Coubard 638:c90ae1400bf2 72 #define FS_SECTION_VARS_END_ADDR NRF_SECTION_VARS_END_ADDR(fs_data)
Vincent Coubard 638:c90ae1400bf2 73
Vincent Coubard 638:c90ae1400bf2 74 /** @} */
Vincent Coubard 638:c90ae1400bf2 75
Vincent Coubard 638:c90ae1400bf2 76
Vincent Coubard 638:c90ae1400bf2 77 /**@brief The command queue element.
Vincent Coubard 638:c90ae1400bf2 78 *
Vincent Coubard 638:c90ae1400bf2 79 * @details Encapsulate details of a command requested to this module.
Vincent Coubard 638:c90ae1400bf2 80 */
Vincent Coubard 638:c90ae1400bf2 81 typedef struct
Vincent Coubard 638:c90ae1400bf2 82 {
Vincent Coubard 638:c90ae1400bf2 83 fs_config_t const * p_config; /**< The configuration of the user who requested the operation. */
Vincent Coubard 638:c90ae1400bf2 84 uint8_t op_code; /**< Operation code. */
Vincent Coubard 638:c90ae1400bf2 85 uint32_t const * p_src; /**< Pointer to the data to be written to flash. The data must be kept in memory until the operation has finished. */
Vincent Coubard 638:c90ae1400bf2 86 uint32_t const * p_addr; /**< Destination of the data in flash. */
Vincent Coubard 638:c90ae1400bf2 87 fs_length_t length_words; /**< Length of the operation */
Vincent Coubard 638:c90ae1400bf2 88 fs_length_t offset; /**< Offset of the operation if operation is done in chunks */
Vincent Coubard 638:c90ae1400bf2 89 } fs_cmd_t;
Vincent Coubard 638:c90ae1400bf2 90
Vincent Coubard 638:c90ae1400bf2 91
Vincent Coubard 638:c90ae1400bf2 92 /**@brief Structure that defines the command queue
Vincent Coubard 638:c90ae1400bf2 93 *
Vincent Coubard 638:c90ae1400bf2 94 * @details This queue holds flash operations requested to the module.
Vincent Coubard 638:c90ae1400bf2 95 * The data to be written must be kept in memory until the write operation is completed,
Vincent Coubard 638:c90ae1400bf2 96 * i.e., a callback indicating completion is received by the application.
Vincent Coubard 638:c90ae1400bf2 97 */
Vincent Coubard 638:c90ae1400bf2 98 typedef struct
Vincent Coubard 638:c90ae1400bf2 99 {
Vincent Coubard 638:c90ae1400bf2 100 uint8_t rp; /**< The current element being processed. */
Vincent Coubard 638:c90ae1400bf2 101 uint8_t count; /**< Number of elements in the queue. */
Vincent Coubard 638:c90ae1400bf2 102 fs_cmd_t cmd[FS_CMD_QUEUE_SIZE]; /**< Array to maintain flash access operation details. */
Vincent Coubard 638:c90ae1400bf2 103 } fs_cmd_queue_t;
Vincent Coubard 638:c90ae1400bf2 104
Vincent Coubard 638:c90ae1400bf2 105
Vincent Coubard 638:c90ae1400bf2 106 static uint8_t m_flags; /**< FStorage status flags. */
Vincent Coubard 638:c90ae1400bf2 107 static fs_cmd_queue_t m_cmd_queue; /**< Flash operation request queue. */
Vincent Coubard 638:c90ae1400bf2 108 static uint16_t m_retry_count = 0; /**< Number of times a single flash operation was retried. */
Vincent Coubard 638:c90ae1400bf2 109
Vincent Coubard 638:c90ae1400bf2 110
Vincent Coubard 638:c90ae1400bf2 111 // Function prototypes
Vincent Coubard 638:c90ae1400bf2 112 static ret_code_t queue_process(void);
Vincent Coubard 638:c90ae1400bf2 113 static ret_code_t queue_process_impl(void);
Vincent Coubard 638:c90ae1400bf2 114 static void app_notify(uint32_t result, fs_cmd_t const * p_cmd);
Vincent Coubard 638:c90ae1400bf2 115
Vincent Coubard 638:c90ae1400bf2 116
Vincent Coubard 638:c90ae1400bf2 117 /**@brief Macro to check that the configuration is non-NULL and within
Vincent Coubard 638:c90ae1400bf2 118 * valid section variable memory bounds.
Vincent Coubard 638:c90ae1400bf2 119 *
Vincent Coubard 638:c90ae1400bf2 120 * @param[in] config Configuration to check.
Vincent Coubard 638:c90ae1400bf2 121 */
Vincent Coubard 638:c90ae1400bf2 122 #define FS_CHECK_CONFIG(config) \
Vincent Coubard 638:c90ae1400bf2 123 ((FS_SECTION_VARS_START_ADDR < config) && (config < FS_SECTION_VARS_END_ADDR))
Vincent Coubard 638:c90ae1400bf2 124
Vincent Coubard 638:c90ae1400bf2 125
Vincent Coubard 638:c90ae1400bf2 126 /**@brief Function to check that the configuration is non-NULL and within
Vincent Coubard 638:c90ae1400bf2 127 * valid section variable memory bounds.
Vincent Coubard 638:c90ae1400bf2 128 *
Vincent Coubard 638:c90ae1400bf2 129 * @param[in] config Configuration to check.
Vincent Coubard 638:c90ae1400bf2 130 */
Vincent Coubard 638:c90ae1400bf2 131 static bool check_config(fs_config_t const * const config)
Vincent Coubard 638:c90ae1400bf2 132 {
Vincent Coubard 638:c90ae1400bf2 133 if (config == NULL)
Vincent Coubard 638:c90ae1400bf2 134 {
Vincent Coubard 638:c90ae1400bf2 135 return false;
Vincent Coubard 638:c90ae1400bf2 136 }
Vincent Coubard 638:c90ae1400bf2 137
Vincent Coubard 638:c90ae1400bf2 138 if ((FS_SECTION_VARS_START_ADDR <= (uint32_t)config) && ((uint32_t)config < FS_SECTION_VARS_END_ADDR))
Vincent Coubard 638:c90ae1400bf2 139 {
Vincent Coubard 638:c90ae1400bf2 140 return true;
Vincent Coubard 638:c90ae1400bf2 141 }
Vincent Coubard 638:c90ae1400bf2 142 else
Vincent Coubard 638:c90ae1400bf2 143 {
Vincent Coubard 638:c90ae1400bf2 144 return false;
Vincent Coubard 638:c90ae1400bf2 145 }
Vincent Coubard 638:c90ae1400bf2 146 }
Vincent Coubard 638:c90ae1400bf2 147
Vincent Coubard 638:c90ae1400bf2 148
Vincent Coubard 638:c90ae1400bf2 149 /**@brief Function to initialize the queue. */
Vincent Coubard 638:c90ae1400bf2 150 static void queue_init(void)
Vincent Coubard 638:c90ae1400bf2 151 {
Vincent Coubard 638:c90ae1400bf2 152 memset(&m_cmd_queue, 0, sizeof(fs_cmd_queue_t));
Vincent Coubard 638:c90ae1400bf2 153 }
Vincent Coubard 638:c90ae1400bf2 154
Vincent Coubard 638:c90ae1400bf2 155
Vincent Coubard 638:c90ae1400bf2 156 /**@brief Function to reset a queue item to its default values.
Vincent Coubard 638:c90ae1400bf2 157 *
Vincent Coubard 638:c90ae1400bf2 158 * @param index Index of the queue element.
Vincent Coubard 638:c90ae1400bf2 159 */
Vincent Coubard 638:c90ae1400bf2 160 static void cmd_reset(uint32_t index)
Vincent Coubard 638:c90ae1400bf2 161 {
Vincent Coubard 638:c90ae1400bf2 162 memset(&m_cmd_queue.cmd[index], 0, sizeof(fs_cmd_t));
Vincent Coubard 638:c90ae1400bf2 163 }
Vincent Coubard 638:c90ae1400bf2 164
Vincent Coubard 638:c90ae1400bf2 165
Vincent Coubard 638:c90ae1400bf2 166 /**@brief Function to enqueue flash access command
Vincent Coubard 638:c90ae1400bf2 167 *
Vincent Coubard 638:c90ae1400bf2 168 * @param[in] config Registered configuration.
Vincent Coubard 638:c90ae1400bf2 169 * @param[in] op_code Operation code.
Vincent Coubard 638:c90ae1400bf2 170 * @param[in] address Destination of the data.
Vincent Coubard 638:c90ae1400bf2 171 * @param[in] p_src Source of data or NULL if n/a.
Vincent Coubard 638:c90ae1400bf2 172 * @param[in] length Length of the data, in 4 byte words.
Vincent Coubard 638:c90ae1400bf2 173 *
Vincent Coubard 638:c90ae1400bf2 174 * @retval NRF_SUCCESS Success. Command enqueued.
Vincent Coubard 638:c90ae1400bf2 175 * @retval NRF_ERROR_NO_MEM Error. Queue is full.
Vincent Coubard 638:c90ae1400bf2 176 * @retval Any error returned by the SoftDevice flash API.
Vincent Coubard 638:c90ae1400bf2 177 */
Vincent Coubard 638:c90ae1400bf2 178 static ret_code_t cmd_enqueue(fs_config_t const * p_config,
Vincent Coubard 638:c90ae1400bf2 179 uint8_t op_code,
Vincent Coubard 638:c90ae1400bf2 180 uint32_t const * p_addr,
Vincent Coubard 638:c90ae1400bf2 181 uint32_t const * p_src,
Vincent Coubard 638:c90ae1400bf2 182 fs_length_t length_words)
Vincent Coubard 638:c90ae1400bf2 183 {
Vincent Coubard 638:c90ae1400bf2 184 fs_cmd_t * p_cmd;
Vincent Coubard 638:c90ae1400bf2 185 uint8_t write_pos;
Vincent Coubard 638:c90ae1400bf2 186
Vincent Coubard 638:c90ae1400bf2 187 if (m_cmd_queue.count == FS_CMD_QUEUE_SIZE - 1)
Vincent Coubard 638:c90ae1400bf2 188 {
Vincent Coubard 638:c90ae1400bf2 189 return NRF_ERROR_NO_MEM;
Vincent Coubard 638:c90ae1400bf2 190 }
Vincent Coubard 638:c90ae1400bf2 191
Vincent Coubard 638:c90ae1400bf2 192 write_pos = (m_cmd_queue.rp + m_cmd_queue.count) % FS_CMD_QUEUE_SIZE;
Vincent Coubard 638:c90ae1400bf2 193
Vincent Coubard 638:c90ae1400bf2 194 p_cmd = &m_cmd_queue.cmd[write_pos];
Vincent Coubard 638:c90ae1400bf2 195
Vincent Coubard 638:c90ae1400bf2 196 p_cmd->p_config = p_config;
Vincent Coubard 638:c90ae1400bf2 197 p_cmd->op_code = op_code;
Vincent Coubard 638:c90ae1400bf2 198 p_cmd->p_src = p_src;
Vincent Coubard 638:c90ae1400bf2 199 p_cmd->p_addr = p_addr;
Vincent Coubard 638:c90ae1400bf2 200 p_cmd->length_words = length_words;
Vincent Coubard 638:c90ae1400bf2 201
Vincent Coubard 638:c90ae1400bf2 202 m_cmd_queue.count++;
Vincent Coubard 638:c90ae1400bf2 203
Vincent Coubard 638:c90ae1400bf2 204 return queue_process();
Vincent Coubard 638:c90ae1400bf2 205 }
Vincent Coubard 638:c90ae1400bf2 206
Vincent Coubard 638:c90ae1400bf2 207
Vincent Coubard 638:c90ae1400bf2 208 /**@brief Function to consume queue item and notify the return value of the operation.
Vincent Coubard 638:c90ae1400bf2 209 *
Vincent Coubard 638:c90ae1400bf2 210 * @details This function will report the result and remove the command from the queue after
Vincent Coubard 638:c90ae1400bf2 211 * notification.
Vincent Coubard 638:c90ae1400bf2 212 */
Vincent Coubard 638:c90ae1400bf2 213 static void cmd_consume(uint32_t result, const fs_cmd_t * p_cmd)
Vincent Coubard 638:c90ae1400bf2 214 {
Vincent Coubard 638:c90ae1400bf2 215 // Consume the current item on the queue.
Vincent Coubard 638:c90ae1400bf2 216 uint8_t rp = m_cmd_queue.rp;
Vincent Coubard 638:c90ae1400bf2 217
Vincent Coubard 638:c90ae1400bf2 218 m_cmd_queue.count--;
Vincent Coubard 638:c90ae1400bf2 219 if (m_cmd_queue.count == 0)
Vincent Coubard 638:c90ae1400bf2 220 {
Vincent Coubard 638:c90ae1400bf2 221 // There are no elements left. Stop processing the queue.
Vincent Coubard 638:c90ae1400bf2 222 m_flags &= ~FS_FLAG_PROCESSING;
Vincent Coubard 638:c90ae1400bf2 223 }
Vincent Coubard 638:c90ae1400bf2 224
Vincent Coubard 638:c90ae1400bf2 225 if (++(m_cmd_queue.rp) == FS_CMD_QUEUE_SIZE)
Vincent Coubard 638:c90ae1400bf2 226 {
Vincent Coubard 638:c90ae1400bf2 227 m_cmd_queue.rp = 0;
Vincent Coubard 638:c90ae1400bf2 228 }
Vincent Coubard 638:c90ae1400bf2 229
Vincent Coubard 638:c90ae1400bf2 230 // Notify upon successful operation.
Vincent Coubard 638:c90ae1400bf2 231 app_notify(result, p_cmd);
Vincent Coubard 638:c90ae1400bf2 232
Vincent Coubard 638:c90ae1400bf2 233 // Reset the queue element.
Vincent Coubard 638:c90ae1400bf2 234 cmd_reset(rp);
Vincent Coubard 638:c90ae1400bf2 235 }
Vincent Coubard 638:c90ae1400bf2 236
Vincent Coubard 638:c90ae1400bf2 237
Vincent Coubard 638:c90ae1400bf2 238 /**@brief Function to store data to flash.
Vincent Coubard 638:c90ae1400bf2 239 *
Vincent Coubard 638:c90ae1400bf2 240 * @param[in] p_cmd The queue element associated with the operation.
Vincent Coubard 638:c90ae1400bf2 241 *
Vincent Coubard 638:c90ae1400bf2 242 * @retval NRF_SUCCESS Success. The request was sent to the SoftDevice.
Vincent Coubard 638:c90ae1400bf2 243 * @retval Any error returned by the SoftDevice flash API.
Vincent Coubard 638:c90ae1400bf2 244 */
Vincent Coubard 638:c90ae1400bf2 245 static __INLINE uint32_t store_execute(fs_cmd_t const * const p_cmd)
Vincent Coubard 638:c90ae1400bf2 246 {
Vincent Coubard 638:c90ae1400bf2 247 // Write in chunks if write-size is larger than FS_MAX_WRITE_SIZE.
Vincent Coubard 638:c90ae1400bf2 248 fs_length_t const length = ((p_cmd->length_words - p_cmd->offset) < FS_MAX_WRITE_SIZE_WORDS) ?
Vincent Coubard 638:c90ae1400bf2 249 (p_cmd->length_words - p_cmd->offset) : FS_MAX_WRITE_SIZE_WORDS;
Vincent Coubard 638:c90ae1400bf2 250
Vincent Coubard 638:c90ae1400bf2 251 return sd_flash_write((uint32_t*)p_cmd->p_addr + p_cmd->offset /* destination */,
Vincent Coubard 638:c90ae1400bf2 252 (uint32_t*)p_cmd->p_src + p_cmd->offset /* source */,
Vincent Coubard 638:c90ae1400bf2 253 length);
Vincent Coubard 638:c90ae1400bf2 254 }
Vincent Coubard 638:c90ae1400bf2 255
Vincent Coubard 638:c90ae1400bf2 256
Vincent Coubard 638:c90ae1400bf2 257 /**@brief Function to erase a page.
Vincent Coubard 638:c90ae1400bf2 258 *
Vincent Coubard 638:c90ae1400bf2 259 * @param[in] p_cmd The queue element associated with the operation.
Vincent Coubard 638:c90ae1400bf2 260 *
Vincent Coubard 638:c90ae1400bf2 261 * @retval NRF_SUCCESS Success. The request was sent to the SoftDevice.
Vincent Coubard 638:c90ae1400bf2 262 * @retval Any error returned by the SoftDevice flash API.
Vincent Coubard 638:c90ae1400bf2 263 */
Vincent Coubard 638:c90ae1400bf2 264 static __INLINE uint32_t erase_execute(fs_cmd_t const * const p_cmd)
Vincent Coubard 638:c90ae1400bf2 265 {
Vincent Coubard 638:c90ae1400bf2 266 // Erase the page.
Vincent Coubard 638:c90ae1400bf2 267 return sd_flash_page_erase((uint32_t)(p_cmd->p_addr + p_cmd->offset) / FS_PAGE_SIZE);
Vincent Coubard 638:c90ae1400bf2 268 }
Vincent Coubard 638:c90ae1400bf2 269
Vincent Coubard 638:c90ae1400bf2 270
Vincent Coubard 638:c90ae1400bf2 271 /**@brief Function to process the current element in the queue and return the result.
Vincent Coubard 638:c90ae1400bf2 272 *
Vincent Coubard 638:c90ae1400bf2 273 * @retval NRF_SUCCESS Success.
Vincent Coubard 638:c90ae1400bf2 274 * @retval NRF_ERROR_FORBIDDEN Error. Undefined command.
Vincent Coubard 638:c90ae1400bf2 275 * @retval Any error returned by the SoftDevice flash API.
Vincent Coubard 638:c90ae1400bf2 276 */
Vincent Coubard 638:c90ae1400bf2 277 static uint32_t queue_process_impl(void)
Vincent Coubard 638:c90ae1400bf2 278 {
Vincent Coubard 638:c90ae1400bf2 279 uint32_t ret;
Vincent Coubard 638:c90ae1400bf2 280
Vincent Coubard 638:c90ae1400bf2 281 fs_cmd_t const * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
Vincent Coubard 638:c90ae1400bf2 282
Vincent Coubard 638:c90ae1400bf2 283 switch (p_cmd->op_code)
Vincent Coubard 638:c90ae1400bf2 284 {
Vincent Coubard 638:c90ae1400bf2 285 case FS_OP_STORE:
Vincent Coubard 638:c90ae1400bf2 286 ret = store_execute(p_cmd);
Vincent Coubard 638:c90ae1400bf2 287 break;
Vincent Coubard 638:c90ae1400bf2 288
Vincent Coubard 638:c90ae1400bf2 289 case FS_OP_ERASE:
Vincent Coubard 638:c90ae1400bf2 290 ret = erase_execute(p_cmd);
Vincent Coubard 638:c90ae1400bf2 291 break;
Vincent Coubard 638:c90ae1400bf2 292
Vincent Coubard 638:c90ae1400bf2 293 case FS_OP_NONE:
Vincent Coubard 638:c90ae1400bf2 294 ret = NRF_SUCCESS;
Vincent Coubard 638:c90ae1400bf2 295 break;
Vincent Coubard 638:c90ae1400bf2 296
Vincent Coubard 638:c90ae1400bf2 297 default:
Vincent Coubard 638:c90ae1400bf2 298 ret = NRF_ERROR_FORBIDDEN;
Vincent Coubard 638:c90ae1400bf2 299 break;
Vincent Coubard 638:c90ae1400bf2 300 }
Vincent Coubard 638:c90ae1400bf2 301
Vincent Coubard 638:c90ae1400bf2 302 return ret;
Vincent Coubard 638:c90ae1400bf2 303 }
Vincent Coubard 638:c90ae1400bf2 304
Vincent Coubard 638:c90ae1400bf2 305
Vincent Coubard 638:c90ae1400bf2 306 /**@brief Starts processing the queue if there are no pending flash operations
Vincent Coubard 638:c90ae1400bf2 307 * for which we are awaiting a callback.
Vincent Coubard 638:c90ae1400bf2 308 */
Vincent Coubard 638:c90ae1400bf2 309 static ret_code_t queue_process(void)
Vincent Coubard 638:c90ae1400bf2 310 {
Vincent Coubard 638:c90ae1400bf2 311 ret_code_t ret = NRF_SUCCESS;
Vincent Coubard 638:c90ae1400bf2 312
Vincent Coubard 638:c90ae1400bf2 313 /** If the queue is not being processed, and there are still
Vincent Coubard 638:c90ae1400bf2 314 * some elements in it, then start processing. */
Vincent Coubard 638:c90ae1400bf2 315 if ( !(m_flags & FS_FLAG_PROCESSING) &&
Vincent Coubard 638:c90ae1400bf2 316 (m_cmd_queue.count > 0))
Vincent Coubard 638:c90ae1400bf2 317 {
Vincent Coubard 638:c90ae1400bf2 318 m_flags |= FS_FLAG_PROCESSING;
Vincent Coubard 638:c90ae1400bf2 319
Vincent Coubard 638:c90ae1400bf2 320 ret = queue_process_impl();
Vincent Coubard 638:c90ae1400bf2 321
Vincent Coubard 638:c90ae1400bf2 322 /** There is ongoing flash-operation which was not
Vincent Coubard 638:c90ae1400bf2 323 * initiated by fstorage. */
Vincent Coubard 638:c90ae1400bf2 324 if (ret == NRF_ERROR_BUSY)
Vincent Coubard 638:c90ae1400bf2 325 {
Vincent Coubard 638:c90ae1400bf2 326 // Wait for a system callback.
Vincent Coubard 638:c90ae1400bf2 327 m_flags |= FS_FLAG_FLASH_REQ_PENDING;
Vincent Coubard 638:c90ae1400bf2 328
Vincent Coubard 638:c90ae1400bf2 329 // Stop processing the queue.
Vincent Coubard 638:c90ae1400bf2 330 m_flags &= ~FS_FLAG_PROCESSING;
Vincent Coubard 638:c90ae1400bf2 331
Vincent Coubard 638:c90ae1400bf2 332 ret = NRF_SUCCESS;
Vincent Coubard 638:c90ae1400bf2 333 }
Vincent Coubard 638:c90ae1400bf2 334 else if (ret != NRF_SUCCESS)
Vincent Coubard 638:c90ae1400bf2 335 {
Vincent Coubard 638:c90ae1400bf2 336 // Another error has occurred.
Vincent Coubard 638:c90ae1400bf2 337 app_notify(ret, &m_cmd_queue.cmd[m_cmd_queue.rp]);
Vincent Coubard 638:c90ae1400bf2 338 }
Vincent Coubard 638:c90ae1400bf2 339 }
Vincent Coubard 638:c90ae1400bf2 340
Vincent Coubard 638:c90ae1400bf2 341 // If we are already processing the queue, return immediately.
Vincent Coubard 638:c90ae1400bf2 342 return ret;
Vincent Coubard 638:c90ae1400bf2 343 }
Vincent Coubard 638:c90ae1400bf2 344
Vincent Coubard 638:c90ae1400bf2 345
Vincent Coubard 638:c90ae1400bf2 346 /**@brief Flash operation success callback handler.
Vincent Coubard 638:c90ae1400bf2 347 *
Vincent Coubard 638:c90ae1400bf2 348 * @details This function updates read/write pointers.
Vincent Coubard 638:c90ae1400bf2 349 * This function resets retry count.
Vincent Coubard 638:c90ae1400bf2 350 */
Vincent Coubard 638:c90ae1400bf2 351 static __INLINE void on_operation_success(void)
Vincent Coubard 638:c90ae1400bf2 352 {
Vincent Coubard 638:c90ae1400bf2 353 fs_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
Vincent Coubard 638:c90ae1400bf2 354
Vincent Coubard 638:c90ae1400bf2 355 m_retry_count = 0;
Vincent Coubard 638:c90ae1400bf2 356
Vincent Coubard 638:c90ae1400bf2 357 switch (p_cmd->op_code)
Vincent Coubard 638:c90ae1400bf2 358 {
Vincent Coubard 638:c90ae1400bf2 359 case FS_OP_STORE:
Vincent Coubard 638:c90ae1400bf2 360 // Update the offset on successful write.
Vincent Coubard 638:c90ae1400bf2 361 p_cmd->offset += FS_MAX_WRITE_SIZE_WORDS;
Vincent Coubard 638:c90ae1400bf2 362 break;
Vincent Coubard 638:c90ae1400bf2 363
Vincent Coubard 638:c90ae1400bf2 364 case FS_OP_ERASE:
Vincent Coubard 638:c90ae1400bf2 365 // Update the offset to correspond to the page that has been erased.
Vincent Coubard 638:c90ae1400bf2 366 p_cmd->offset += FS_PAGE_SIZE_WORDS;
Vincent Coubard 638:c90ae1400bf2 367 break;
Vincent Coubard 638:c90ae1400bf2 368 }
Vincent Coubard 638:c90ae1400bf2 369
Vincent Coubard 638:c90ae1400bf2 370 // If offset is equal to or larger than length, then the operation has finished.
Vincent Coubard 638:c90ae1400bf2 371 if (p_cmd->offset >= p_cmd->length_words)
Vincent Coubard 638:c90ae1400bf2 372 {
Vincent Coubard 638:c90ae1400bf2 373 cmd_consume(NRF_SUCCESS, p_cmd);
Vincent Coubard 638:c90ae1400bf2 374 }
Vincent Coubard 638:c90ae1400bf2 375
Vincent Coubard 638:c90ae1400bf2 376 queue_process();
Vincent Coubard 638:c90ae1400bf2 377 }
Vincent Coubard 638:c90ae1400bf2 378
Vincent Coubard 638:c90ae1400bf2 379
Vincent Coubard 638:c90ae1400bf2 380 /**@brief Flash operation failure callback handler.
Vincent Coubard 638:c90ae1400bf2 381 *
Vincent Coubard 638:c90ae1400bf2 382 * @details Function to keep track of retries and notify failures.
Vincent Coubard 638:c90ae1400bf2 383 */
Vincent Coubard 638:c90ae1400bf2 384 static __INLINE void on_operation_failure(uint32_t sys_evt)
Vincent Coubard 638:c90ae1400bf2 385 {
Vincent Coubard 638:c90ae1400bf2 386 const fs_cmd_t * p_cmd;
Vincent Coubard 638:c90ae1400bf2 387
Vincent Coubard 638:c90ae1400bf2 388 if (++m_retry_count > FS_CMD_MAX_RETRIES)
Vincent Coubard 638:c90ae1400bf2 389 {
Vincent Coubard 638:c90ae1400bf2 390 p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp];
Vincent Coubard 638:c90ae1400bf2 391 cmd_consume(NRF_ERROR_TIMEOUT, p_cmd);
Vincent Coubard 638:c90ae1400bf2 392 }
Vincent Coubard 638:c90ae1400bf2 393
Vincent Coubard 638:c90ae1400bf2 394 queue_process();
Vincent Coubard 638:c90ae1400bf2 395 }
Vincent Coubard 638:c90ae1400bf2 396
Vincent Coubard 638:c90ae1400bf2 397
Vincent Coubard 638:c90ae1400bf2 398 /**@brief Function to notify users.
Vincent Coubard 638:c90ae1400bf2 399 *
Vincent Coubard 638:c90ae1400bf2 400 * @param[in] result Result of the flash operation.
Vincent Coubard 638:c90ae1400bf2 401 * @param[in] p_cmd The command associated with the callback.
Vincent Coubard 638:c90ae1400bf2 402 */
Vincent Coubard 638:c90ae1400bf2 403 static void app_notify(uint32_t result, fs_cmd_t const * const p_cmd)
Vincent Coubard 638:c90ae1400bf2 404 {
Vincent Coubard 638:c90ae1400bf2 405 p_cmd->p_config->cb(p_cmd->op_code, result, p_cmd->p_addr, p_cmd->length_words);
Vincent Coubard 638:c90ae1400bf2 406 }
Vincent Coubard 638:c90ae1400bf2 407
Vincent Coubard 638:c90ae1400bf2 408
Vincent Coubard 638:c90ae1400bf2 409 ret_code_t fs_init(void)
Vincent Coubard 638:c90ae1400bf2 410 {
Vincent Coubard 638:c90ae1400bf2 411 uint16_t lowest_index = 0;
Vincent Coubard 638:c90ae1400bf2 412 uint16_t lowest_order = 0xFFFF;
Vincent Coubard 638:c90ae1400bf2 413 uint32_t * current_end = (uint32_t*)FS_PAGE_END_ADDR;
Vincent Coubard 638:c90ae1400bf2 414 uint32_t num_left = FS_SECTION_VARS_COUNT;
Vincent Coubard 638:c90ae1400bf2 415
Vincent Coubard 638:c90ae1400bf2 416 queue_init();
Vincent Coubard 638:c90ae1400bf2 417
Vincent Coubard 638:c90ae1400bf2 418 /** Assign pages to registered users, beginning with the ones with the lowest
Vincent Coubard 638:c90ae1400bf2 419 * order, which will be assigned pages with the lowest memory address. */
Vincent Coubard 638:c90ae1400bf2 420 do
Vincent Coubard 638:c90ae1400bf2 421 {
Vincent Coubard 638:c90ae1400bf2 422 fs_config_t * p_config;
Vincent Coubard 638:c90ae1400bf2 423 for (uint16_t i = 0; i < FS_SECTION_VARS_COUNT; i++)
Vincent Coubard 638:c90ae1400bf2 424 {
Vincent Coubard 638:c90ae1400bf2 425 p_config = FS_SECTION_VARS_GET(i);
Vincent Coubard 638:c90ae1400bf2 426
Vincent Coubard 638:c90ae1400bf2 427 // Skip the ones which have the end-address already set.
Vincent Coubard 638:c90ae1400bf2 428 if (p_config->p_end_addr != NULL)
Vincent Coubard 638:c90ae1400bf2 429 continue;
Vincent Coubard 638:c90ae1400bf2 430
Vincent Coubard 638:c90ae1400bf2 431 if (p_config->page_order < lowest_order)
Vincent Coubard 638:c90ae1400bf2 432 {
Vincent Coubard 638:c90ae1400bf2 433 lowest_order = p_config->page_order;
Vincent Coubard 638:c90ae1400bf2 434 lowest_index = i;
Vincent Coubard 638:c90ae1400bf2 435 }
Vincent Coubard 638:c90ae1400bf2 436 }
Vincent Coubard 638:c90ae1400bf2 437
Vincent Coubard 638:c90ae1400bf2 438 p_config = FS_SECTION_VARS_GET(lowest_index);
Vincent Coubard 638:c90ae1400bf2 439
Vincent Coubard 638:c90ae1400bf2 440 p_config->p_end_addr = current_end;
Vincent Coubard 638:c90ae1400bf2 441 p_config->p_start_addr = p_config->p_end_addr - (p_config->num_pages * FS_PAGE_SIZE_WORDS);
Vincent Coubard 638:c90ae1400bf2 442
Vincent Coubard 638:c90ae1400bf2 443 current_end = p_config->p_start_addr;
Vincent Coubard 638:c90ae1400bf2 444 lowest_order = 0xFFFF;
Vincent Coubard 638:c90ae1400bf2 445
Vincent Coubard 638:c90ae1400bf2 446 } while ( --num_left > 0 );
Vincent Coubard 638:c90ae1400bf2 447
Vincent Coubard 638:c90ae1400bf2 448 m_flags |= FS_FLAG_INIT;
Vincent Coubard 638:c90ae1400bf2 449
Vincent Coubard 638:c90ae1400bf2 450 return NRF_SUCCESS;
Vincent Coubard 638:c90ae1400bf2 451 }
Vincent Coubard 638:c90ae1400bf2 452
Vincent Coubard 638:c90ae1400bf2 453
Vincent Coubard 638:c90ae1400bf2 454 ret_code_t fs_store(fs_config_t const * p_config,
Vincent Coubard 638:c90ae1400bf2 455 uint32_t const * p_addr,
Vincent Coubard 638:c90ae1400bf2 456 uint32_t const * const p_data,
Vincent Coubard 638:c90ae1400bf2 457 fs_length_t length_words)
Vincent Coubard 638:c90ae1400bf2 458 {
Vincent Coubard 638:c90ae1400bf2 459 if ((m_flags & FS_FLAG_INIT) == 0)
Vincent Coubard 638:c90ae1400bf2 460 {
Vincent Coubard 638:c90ae1400bf2 461 return NRF_ERROR_INVALID_STATE;
Vincent Coubard 638:c90ae1400bf2 462 }
Vincent Coubard 638:c90ae1400bf2 463
Vincent Coubard 638:c90ae1400bf2 464 if (!check_config(p_config))
Vincent Coubard 638:c90ae1400bf2 465 {
Vincent Coubard 638:c90ae1400bf2 466 return NRF_ERROR_FORBIDDEN;
Vincent Coubard 638:c90ae1400bf2 467 }
Vincent Coubard 638:c90ae1400bf2 468
Vincent Coubard 638:c90ae1400bf2 469 if (!is_word_aligned(p_addr))
Vincent Coubard 638:c90ae1400bf2 470 {
Vincent Coubard 638:c90ae1400bf2 471 return NRF_ERROR_INVALID_ADDR;
Vincent Coubard 638:c90ae1400bf2 472 }
Vincent Coubard 638:c90ae1400bf2 473
Vincent Coubard 638:c90ae1400bf2 474 // Check that the erase operation is on pages owned by this user (configuration).
Vincent Coubard 638:c90ae1400bf2 475 if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr))
Vincent Coubard 638:c90ae1400bf2 476 {
Vincent Coubard 638:c90ae1400bf2 477 return NRF_ERROR_INVALID_ADDR;
Vincent Coubard 638:c90ae1400bf2 478 }
Vincent Coubard 638:c90ae1400bf2 479
Vincent Coubard 638:c90ae1400bf2 480 return cmd_enqueue(p_config, FS_OP_STORE, p_addr, p_data, length_words);
Vincent Coubard 638:c90ae1400bf2 481 }
Vincent Coubard 638:c90ae1400bf2 482
Vincent Coubard 638:c90ae1400bf2 483
Vincent Coubard 638:c90ae1400bf2 484 ret_code_t fs_erase(fs_config_t const * p_config,
Vincent Coubard 638:c90ae1400bf2 485 uint32_t * const p_addr,
Vincent Coubard 638:c90ae1400bf2 486 fs_length_t const length_words)
Vincent Coubard 638:c90ae1400bf2 487 {
Vincent Coubard 638:c90ae1400bf2 488 if ((m_flags & FS_FLAG_INIT) == 0)
Vincent Coubard 638:c90ae1400bf2 489 {
Vincent Coubard 638:c90ae1400bf2 490 return NRF_ERROR_INVALID_STATE;
Vincent Coubard 638:c90ae1400bf2 491 }
Vincent Coubard 638:c90ae1400bf2 492
Vincent Coubard 638:c90ae1400bf2 493 if (!check_config(p_config))
Vincent Coubard 638:c90ae1400bf2 494 {
Vincent Coubard 638:c90ae1400bf2 495 return NRF_ERROR_FORBIDDEN;
Vincent Coubard 638:c90ae1400bf2 496 }
Vincent Coubard 638:c90ae1400bf2 497
Vincent Coubard 638:c90ae1400bf2 498 /** Check that the address is aligned on a page boundary and the length to erase
Vincent Coubard 638:c90ae1400bf2 499 * is a multiple of the page size. */
Vincent Coubard 638:c90ae1400bf2 500 if (((uint32_t)p_addr & (FS_PAGE_SIZE - 1)) ||
Vincent Coubard 638:c90ae1400bf2 501 (length_words & (FS_PAGE_SIZE_WORDS - 1)))
Vincent Coubard 638:c90ae1400bf2 502 {
Vincent Coubard 638:c90ae1400bf2 503 return NRF_ERROR_INVALID_ADDR;
Vincent Coubard 638:c90ae1400bf2 504 }
Vincent Coubard 638:c90ae1400bf2 505
Vincent Coubard 638:c90ae1400bf2 506 // Check that the erase operation is on pages owned by this user (configuration).
Vincent Coubard 638:c90ae1400bf2 507 if ((p_addr < p_config->p_start_addr) || ((p_addr + length_words) > p_config->p_end_addr))
Vincent Coubard 638:c90ae1400bf2 508 {
Vincent Coubard 638:c90ae1400bf2 509 return NRF_ERROR_INVALID_ADDR;
Vincent Coubard 638:c90ae1400bf2 510 }
Vincent Coubard 638:c90ae1400bf2 511
Vincent Coubard 638:c90ae1400bf2 512 return cmd_enqueue(p_config, FS_OP_ERASE, p_addr, NULL, length_words);
Vincent Coubard 638:c90ae1400bf2 513 }
Vincent Coubard 638:c90ae1400bf2 514
Vincent Coubard 638:c90ae1400bf2 515
Vincent Coubard 638:c90ae1400bf2 516 /**@brief Function to handle system events from the SoftDevice.
Vincent Coubard 638:c90ae1400bf2 517 *
Vincent Coubard 638:c90ae1400bf2 518 * @details This function should be dispatched system events if any of the modules used by
Vincent Coubard 638:c90ae1400bf2 519 * the application rely on FStorage. Examples include @ref Peer Manager and
Vincent Coubard 638:c90ae1400bf2 520 * @ref Flash Data Storage.
Vincent Coubard 638:c90ae1400bf2 521 *
Vincent Coubard 638:c90ae1400bf2 522 * @param[in] sys_evt System Event received.
Vincent Coubard 638:c90ae1400bf2 523 */
Vincent Coubard 638:c90ae1400bf2 524 void fs_sys_event_handler(uint32_t sys_evt)
Vincent Coubard 638:c90ae1400bf2 525 {
Vincent Coubard 638:c90ae1400bf2 526 if (m_flags & FS_FLAG_PROCESSING)
Vincent Coubard 638:c90ae1400bf2 527 {
Vincent Coubard 638:c90ae1400bf2 528 /** A flash operation was initiated by this module.
Vincent Coubard 638:c90ae1400bf2 529 * Handle its result. */
Vincent Coubard 638:c90ae1400bf2 530 switch (sys_evt)
Vincent Coubard 638:c90ae1400bf2 531 {
Vincent Coubard 638:c90ae1400bf2 532 case NRF_EVT_FLASH_OPERATION_SUCCESS:
Vincent Coubard 638:c90ae1400bf2 533 on_operation_success();
Vincent Coubard 638:c90ae1400bf2 534 break;
Vincent Coubard 638:c90ae1400bf2 535
Vincent Coubard 638:c90ae1400bf2 536 case NRF_EVT_FLASH_OPERATION_ERROR:
Vincent Coubard 638:c90ae1400bf2 537 on_operation_failure(sys_evt);
Vincent Coubard 638:c90ae1400bf2 538 break;
Vincent Coubard 638:c90ae1400bf2 539 }
Vincent Coubard 638:c90ae1400bf2 540 }
Vincent Coubard 638:c90ae1400bf2 541 else if ((m_flags & FS_FLAG_FLASH_REQ_PENDING))
Vincent Coubard 638:c90ae1400bf2 542 {
Vincent Coubard 638:c90ae1400bf2 543 /** A flash operation was initiated outside this module.
Vincent Coubard 638:c90ae1400bf2 544 * We have now receveid a callback which indicates it has
Vincent Coubard 638:c90ae1400bf2 545 * finished. Clear the FS_FLAG_FLASH_REQ_PENDING flag. */
Vincent Coubard 638:c90ae1400bf2 546 m_flags &= ~FS_FLAG_FLASH_REQ_PENDING;
Vincent Coubard 638:c90ae1400bf2 547
Vincent Coubard 638:c90ae1400bf2 548 // Resume processing the queue, if necessary.
Vincent Coubard 638:c90ae1400bf2 549 queue_process();
Vincent Coubard 638:c90ae1400bf2 550 }
Vincent Coubard 638:c90ae1400bf2 551 }
Vincent Coubard 638:c90ae1400bf2 552
Vincent Coubard 638:c90ae1400bf2 553
Vincent Coubard 638:c90ae1400bf2 554 // Just for testing out section vars (across many compilers).
Vincent Coubard 638:c90ae1400bf2 555 void fs_debug_print()
Vincent Coubard 638:c90ae1400bf2 556 {
Vincent Coubard 638:c90ae1400bf2 557 printf("fs start address: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_START_ADDR);
Vincent Coubard 638:c90ae1400bf2 558 printf("fs end address: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_END_ADDR);
Vincent Coubard 638:c90ae1400bf2 559 printf("Num items: 0x%08lx\r\n", (unsigned long)FS_SECTION_VARS_COUNT);
Vincent Coubard 638:c90ae1400bf2 560 printf("===== ITEMS %lu =====\r\n", (unsigned long)FS_SECTION_VARS_COUNT);
Vincent Coubard 638:c90ae1400bf2 561
Vincent Coubard 638:c90ae1400bf2 562 for(int i = 0; i < FS_SECTION_VARS_COUNT; i++)
Vincent Coubard 638:c90ae1400bf2 563 {
Vincent Coubard 638:c90ae1400bf2 564 fs_config_t* config = FS_SECTION_VARS_GET(i);
Vincent Coubard 638:c90ae1400bf2 565 printf( "Address: 0x%08lx, CB: 0x%08lx\r\n",
Vincent Coubard 638:c90ae1400bf2 566 (unsigned long)config, (unsigned long)config->cb );
Vincent Coubard 638:c90ae1400bf2 567 }
Vincent Coubard 638:c90ae1400bf2 568 printf("\r\n");
Vincent Coubard 638:c90ae1400bf2 569 }