background_dfu_block.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456
  1. /**
  2. * Copyright (c) 2017 - 2018, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. /** @file
  41. *
  42. * @defgroup background_dfu_block background_dfu_block.c
  43. * @{
  44. * @ingroup background_dfu
  45. * @brief Background DFU block handling implementation.
  46. *
  47. */
  48. #include "background_dfu_block.h"
  49. #include <assert.h>
  50. #include "sdk_config.h"
  51. #include "app_scheduler.h"
  52. #include "background_dfu_operation.h"
  53. #include "compiler_abstraction.h"
  54. #include "nrf_dfu_handling_error.h"
  55. #define NRF_LOG_MODULE_NAME background_dfu
  56. #define NRF_LOG_LEVEL BACKGROUND_DFU_CONFIG_LOG_LEVEL
  57. #define NRF_LOG_INFO_COLOR BACKGROUND_DFU_CONFIG_INFO_COLOR
  58. #define NRF_LOG_DEBUG_COLOR BACKGROUND_DFU_CONFIG_DEBUG_COLOR
  59. #include "nrf_log.h"
  60. #define BITMAP_BYTE_FROM_INDEX(index) ((index) / 8)
  61. #define BITMAP_BIT_FROM_INDEX(index) (7 - ((index) % 8))
  62. static void block_buffer_store(background_dfu_block_manager_t * p_bm);
  63. /**@brief Convert block number to bitmap index.
  64. *
  65. * @param[in] block_num Block number.
  66. *
  67. * @return Corresponding index.
  68. */
  69. static __INLINE uint16_t block_num_to_index(uint32_t block_num)
  70. {
  71. return block_num % BLOCKS_PER_BUFFER;
  72. }
  73. /**@brief Set a bit in a bitmap.
  74. *
  75. * @param[inout] p_bitmap A pointer to the bitmap.
  76. * @param[in] index Bit index to set.
  77. */
  78. static __INLINE void set_bitmap_bit(uint8_t * p_bitmap, uint16_t index)
  79. {
  80. p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] |= (0x01 << BITMAP_BIT_FROM_INDEX(index));
  81. }
  82. /**@brief Clear a bit in a bitmap.
  83. *
  84. * @param[inout] p_bitmap A pointer to the bitmap.
  85. * @param[in] index Bit index to clear.
  86. */
  87. static __INLINE void clear_bitmap_bit(uint8_t * p_bitmap, uint16_t index)
  88. {
  89. p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] &= ~((uint8_t)(0x01 << BITMAP_BIT_FROM_INDEX(index)));
  90. }
  91. /**@brief Check if a bit in a bitmap is set.
  92. *
  93. * @param[inout] p_bitmap A pointer to the bitmap.
  94. * @param[in] index Bit index to check.
  95. *
  96. * @return True if bit is set, false otherwise.
  97. */
  98. static __INLINE bool is_block_present(const uint8_t * p_bitmap, uint16_t index)
  99. {
  100. return (p_bitmap[BITMAP_BYTE_FROM_INDEX(index)] >> BITMAP_BIT_FROM_INDEX(index)) & 0x01;
  101. }
  102. /**
  103. * @brief A callback function for DFU operation.
  104. */
  105. static void dfu_operation_callback(nrf_dfu_response_t * p_res, void * p_context)
  106. {
  107. background_dfu_block_manager_t * p_bm = (background_dfu_block_manager_t *)p_context;
  108. ret_code_t res_code;
  109. if (p_res->result != NRF_DFU_RES_CODE_SUCCESS)
  110. {
  111. p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context);
  112. }
  113. else
  114. {
  115. switch (p_res->request)
  116. {
  117. case NRF_DFU_OP_OBJECT_CREATE:
  118. {
  119. // Object created, write respective block.
  120. uint32_t current_size = p_bm->currently_stored_block * DEFAULT_BLOCK_SIZE;
  121. uint16_t data_offset = block_num_to_index(p_bm->currently_stored_block) * DEFAULT_BLOCK_SIZE;
  122. uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size));
  123. res_code = background_dfu_op_write(p_bm->data + data_offset,
  124. store_size,
  125. dfu_operation_callback,
  126. p_bm);
  127. if (res_code != NRF_SUCCESS)
  128. {
  129. NRF_LOG_ERROR("Failed to store block (b:%d c:%d).",
  130. p_bm->currently_stored_block,
  131. p_bm->current_block);
  132. p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context);
  133. }
  134. break;
  135. }
  136. case NRF_DFU_OP_OBJECT_WRITE:
  137. if (!((p_bm->currently_stored_block + 1) % BLOCKS_PER_DFU_OBJECT) ||
  138. ((p_bm->currently_stored_block + 1) == BLOCKS_PER_SIZE(p_bm->image_size)))
  139. {
  140. res_code = background_dfu_op_crc(dfu_operation_callback, p_bm);
  141. if (res_code != NRF_SUCCESS)
  142. {
  143. NRF_LOG_ERROR("Failed to store block (b:%d c:%d).",
  144. p_bm->currently_stored_block,
  145. p_bm->current_block);
  146. p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context);
  147. }
  148. }
  149. else
  150. {
  151. p_bm->last_block_stored = p_bm->currently_stored_block;
  152. clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block));
  153. p_bm->currently_stored_block = INVALID_BLOCK_NUMBER;
  154. p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context);
  155. block_buffer_store(p_bm);
  156. }
  157. break;
  158. case NRF_DFU_OP_CRC_GET:
  159. res_code = background_dfu_op_execute(dfu_operation_callback, p_bm);
  160. if (res_code != NRF_SUCCESS)
  161. {
  162. NRF_LOG_ERROR("Failed to store block (b:%d c:%d).",
  163. p_bm->currently_stored_block,
  164. p_bm->current_block);
  165. p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context);
  166. }
  167. break;
  168. case NRF_DFU_OP_OBJECT_EXECUTE:
  169. p_bm->last_block_stored = p_bm->currently_stored_block;
  170. clear_bitmap_bit(p_bm->bitmap, block_num_to_index(p_bm->currently_stored_block));
  171. p_bm->currently_stored_block = INVALID_BLOCK_NUMBER;
  172. p_bm->result_handler(BACKGROUND_DFU_BLOCK_SUCCESS, p_bm->p_context);
  173. block_buffer_store(p_bm);
  174. break;
  175. default:
  176. ASSERT(false);
  177. }
  178. }
  179. }
  180. /**@brief Store a block from the buffer in a flash.
  181. *
  182. * @param[inout] p_bm A pointer to the block manager.
  183. * @param[in] p_block A block number to store.
  184. *
  185. * @return NRF_SUCCESS on success, an error code is returned otherwise.
  186. */
  187. static ret_code_t block_store(background_dfu_block_manager_t * p_bm, uint32_t block_num)
  188. {
  189. p_bm->currently_stored_block = block_num;
  190. ret_code_t res_code = NRF_SUCCESS;
  191. uint32_t current_size = block_num * DEFAULT_BLOCK_SIZE;
  192. do
  193. {
  194. // Initialize DFU object if needed.
  195. if (!(block_num % BLOCKS_PER_DFU_OBJECT))
  196. {
  197. uint32_t object_size = MIN(DEFAULT_DFU_OBJECT_SIZE, (p_bm->image_size - current_size));
  198. res_code = background_dfu_op_create(p_bm->image_type,
  199. object_size,
  200. dfu_operation_callback,
  201. p_bm);
  202. break;
  203. }
  204. // Store block.
  205. uint16_t data_offset = block_num_to_index(block_num) * DEFAULT_BLOCK_SIZE;
  206. uint16_t store_size = MIN(DEFAULT_BLOCK_SIZE, (p_bm->image_size - current_size));
  207. res_code = background_dfu_op_write(p_bm->data + data_offset,
  208. store_size,
  209. dfu_operation_callback,
  210. p_bm);
  211. } while (0);
  212. return res_code;
  213. }
  214. /**@brief Check if block manager is busy storing a block.
  215. *
  216. * @param[inout] p_bm A pointer to the block manager.
  217. *
  218. */
  219. static bool is_block_manager_busy(background_dfu_block_manager_t * p_bm)
  220. {
  221. return p_bm->currently_stored_block >= 0;
  222. }
  223. /**@brief Store any valid blocks from the buffer in a flash.
  224. *
  225. * @param[inout] p_bm A pointer to the block manager.
  226. *
  227. */
  228. static void block_buffer_store(background_dfu_block_manager_t * p_bm)
  229. {
  230. ret_code_t res_code = NRF_SUCCESS;
  231. if (!is_block_manager_busy(p_bm))
  232. {
  233. if (p_bm->last_block_stored < p_bm->current_block)
  234. {
  235. int32_t block = p_bm->last_block_stored + 1;
  236. if (is_block_present(p_bm->bitmap, block_num_to_index(block)))
  237. {
  238. NRF_LOG_INFO("Storing block (b:%d c:%d).", block, p_bm->current_block);
  239. // There is a block to store.
  240. res_code = block_store(p_bm, block);
  241. if (res_code != NRF_SUCCESS)
  242. {
  243. NRF_LOG_ERROR("Failed to store block (b:%d c:%d).", block, p_bm->current_block);
  244. p_bm->result_handler(BACKGROUND_DFU_BLOCK_STORE_ERROR, p_bm->p_context);
  245. }
  246. }
  247. else
  248. {
  249. NRF_LOG_WARNING("Gap encountered - quit (b:%d c:%d).", block, p_bm->current_block);
  250. }
  251. }
  252. }
  253. }
  254. /**
  255. * @brief A callback function for scheduling DFU block operations.
  256. */
  257. static void block_store_scheduled(void * p_evt, uint16_t event_length)
  258. {
  259. UNUSED_PARAMETER(event_length);
  260. background_dfu_block_manager_t * p_bm = *((background_dfu_block_manager_t **)p_evt);
  261. block_buffer_store(p_bm);
  262. }
  263. /**@brief Copy block data to the buffer.
  264. *
  265. * @param[inout] p_bm A pointer to the block manager.
  266. * @param[in] p_block A pointer to the block.
  267. */
  268. static void block_buffer_add(background_dfu_block_manager_t * p_bm,
  269. const background_dfu_block_t * p_block)
  270. {
  271. uint16_t index = block_num_to_index(p_block->number);
  272. memcpy(p_bm->data + index * DEFAULT_BLOCK_SIZE, p_block->p_payload, DEFAULT_BLOCK_SIZE);
  273. set_bitmap_bit(p_bm->bitmap, index);
  274. if (p_bm->current_block < (int32_t)p_block->number)
  275. {
  276. p_bm->current_block = (int32_t)p_block->number;
  277. }
  278. // Schedule block store.
  279. UNUSED_RETURN_VALUE(app_sched_event_put(&p_bm, sizeof(p_bm), block_store_scheduled));
  280. }
  281. /***************************************************************************************************
  282. * @section Public
  283. **************************************************************************************************/
  284. void block_manager_init(background_dfu_block_manager_t * p_bm,
  285. uint32_t object_type,
  286. uint32_t object_size,
  287. int32_t initial_block,
  288. block_manager_result_notify_t result_handler,
  289. void * p_context)
  290. {
  291. p_bm->image_type = object_type;
  292. p_bm->image_size = object_size;
  293. p_bm->last_block_stored = p_bm->current_block = initial_block - 1;
  294. p_bm->result_handler = result_handler;
  295. p_bm->p_context = p_context;
  296. p_bm->currently_stored_block = INVALID_BLOCK_NUMBER;
  297. memset(p_bm->bitmap, 0, sizeof(p_bm->bitmap));
  298. }
  299. background_dfu_block_result_t block_manager_block_process(background_dfu_block_manager_t * p_bm,
  300. const background_dfu_block_t * p_block)
  301. {
  302. /*
  303. * Possible scenarios:
  304. * 1) We receive a block older than our last stored block - simply ignore it.
  305. * 2) We receive a block that fits within current buffer range - process it.
  306. * 3) We receive a block that exceeds current buffer range - abort DFU as we won't be able to catch-up.
  307. */
  308. if (p_block->size != DEFAULT_BLOCK_SIZE)
  309. {
  310. NRF_LOG_WARNING("Block with incorrect size received (s:%d n:%d).",
  311. p_block->size, p_block->number);
  312. return BACKGROUND_DFU_BLOCK_IGNORE;
  313. }
  314. if ((int32_t)p_block->number <= p_bm->last_block_stored)
  315. {
  316. NRF_LOG_WARNING("Ignoring block that already was stored(o:%d n:%d).",
  317. p_bm->last_block_stored, p_block->number);
  318. return BACKGROUND_DFU_BLOCK_IGNORE;
  319. }
  320. if ((int32_t)p_block->number > p_bm->last_block_stored + BLOCKS_PER_BUFFER)
  321. {
  322. NRF_LOG_WARNING("Too many blocks missed - abort DFU (o:%d n:%d).",
  323. p_bm->last_block_stored, p_block->number);
  324. return BACKGROUND_DFU_BLOCK_INVALID;
  325. }
  326. // Block fits within current buffer - copy it into the buffer and update the current block if most
  327. // recent block was received.
  328. block_buffer_add(p_bm, p_block);
  329. return BACKGROUND_DFU_BLOCK_SUCCESS;
  330. }
  331. bool block_manager_is_image_complete(const background_dfu_block_manager_t * p_bm)
  332. {
  333. uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size);
  334. NRF_LOG_DEBUG("Is image complete (o:%d n:%d).", p_bm->last_block_stored, image_blocks);
  335. if (p_bm->last_block_stored + 1 == image_blocks)
  336. {
  337. return true;
  338. }
  339. return false;
  340. }
  341. bool block_manager_request_bitmap_get(const background_dfu_block_manager_t * p_bm,
  342. background_dfu_request_bitmap_t * p_req_bmp)
  343. {
  344. if (p_bm->current_block > p_bm->last_block_stored)
  345. {
  346. memset(p_req_bmp, 0, sizeof(*p_req_bmp));
  347. p_req_bmp->offset = p_bm->last_block_stored + 1;
  348. p_req_bmp->size = (p_bm->current_block - p_bm->last_block_stored + 7) / 8;
  349. for (uint16_t block = p_req_bmp->offset; block <= p_bm->current_block; block++)
  350. {
  351. if (!is_block_present(p_bm->bitmap, block_num_to_index(block)))
  352. {
  353. set_bitmap_bit(p_req_bmp->bitmap, block - p_req_bmp->offset);
  354. }
  355. }
  356. // Clip empty bytes at the end.
  357. while ((p_req_bmp->size > 0) && (p_req_bmp->bitmap[p_req_bmp->size - 1] == 0))
  358. {
  359. p_req_bmp->size--;
  360. }
  361. if (p_req_bmp->size == 0)
  362. {
  363. return false;
  364. }
  365. return true;
  366. }
  367. return false;
  368. }
  369. bool block_manager_increment_current_block(background_dfu_block_manager_t * p_bm)
  370. {
  371. uint32_t image_blocks = BLOCKS_PER_SIZE(p_bm->image_size);
  372. if (p_bm->current_block + 1 == image_blocks)
  373. {
  374. // Already on last block.
  375. return false;
  376. }
  377. else
  378. {
  379. p_bm->current_block++;
  380. }
  381. return true;
  382. }
  383. int32_t block_manager_get_current_block(const background_dfu_block_manager_t * p_bm)
  384. {
  385. return p_bm->current_block;
  386. }