/**@brief Flash operation success callback handler. * * @details This function updates read/write pointers. * This function resets retry count. */ static void on_operation_success(fs_cmd_t const * const p_cmd) { m_retry_count = 0; switch (p_cmd->op_code) { case FS_OP_STORE: // If offset is equal to or larger than length, then the operation has finished. if (p_cmd->store.offset >= p_cmd->store.length_words) { app_notify(p_cmd, NRF_SUCCESS); queue_advance(); } break; case FS_OP_ERASE: if (p_cmd->erase.pages_erased == p_cmd->erase.pages_to_erase) { app_notify(p_cmd, NRF_SUCCESS); queue_advance(); } break; default: break; } }
/** * @brief Function for handling of system events from SoftDevice. * * @param[in] sys_evt System event received. */ void pstorage_sys_event_handler(uint32_t sys_evt) { uint32_t retval = NRF_SUCCESS; // The event shall only be processed if requested by this module. if (m_cmd_queue.flash_access == true) { cmd_queue_element_t * p_cmd; m_cmd_queue.flash_access = false; switch (sys_evt) { case NRF_EVT_FLASH_OPERATION_SUCCESS: { p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; m_round_val++; bool command_finished = ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size); if (command_finished) { uint8_t queue_rp = m_cmd_queue.rp; m_round_val = 0; m_cmd_queue.count--; m_cmd_queue.rp++; if (m_cmd_queue.rp >= PSTORAGE_CMD_QUEUE_SIZE) { m_cmd_queue.rp -= PSTORAGE_CMD_QUEUE_SIZE; } app_notify(retval, &m_cmd_queue.cmd[queue_rp]); // Initialize/free the element as it is now processed. cmd_queue_element_init(queue_rp); } // Schedule any queued flash access operations. retval = cmd_queue_dequeue(); if (retval != NRF_SUCCESS) { app_notify(retval, &m_cmd_queue.cmd[m_cmd_queue.rp]); } } break; case NRF_EVT_FLASH_OPERATION_ERROR: app_notify(NRF_ERROR_TIMEOUT, &m_cmd_queue.cmd[m_cmd_queue.rp]); break; default: // No implementation needed. break; } } }
uint32_t pstorage_clear(pstorage_handle_t * p_dest, pstorage_size_t size) { uint32_t page_addr; uint32_t retval; uint16_t page_count; VERIFY_MODULE_INITIALIZED(); NULL_PARAM_CHECK(p_dest); MODULE_ID_RANGE_CHECK(p_dest); page_addr = p_dest->block_id / BLE_FLASH_PAGE_SIZE; retval = NRF_SUCCESS; for (page_count = 0; page_count < m_app_table[p_dest->module_id].no_of_pages; page_count++) { retval = ble_flash_page_erase(page_addr); page_addr++; if (retval != NRF_SUCCESS) { break; } } app_notify(p_dest, NULL, PSTORAGE_CLEAR_OP_CODE, size, retval); return retval; }
uint32_t pstorage_load(uint8_t * p_dest, pstorage_handle_t * p_src, pstorage_size_t size, pstorage_size_t offset) { VERIFY_MODULE_INITIALIZED(); NULL_PARAM_CHECK(p_src); NULL_PARAM_CHECK(p_dest); MODULE_ID_RANGE_CHECK (p_src); BLOCK_ID_RANGE_CHECK(p_src); SIZE_CHECK(p_src,size); OFFSET_CHECK(p_src,offset,size); // Verify word alignment. if ((!is_word_aligned(p_dest)) || (!is_word_aligned((void *)(uint32_t)offset))) { return NRF_ERROR_INVALID_ADDR; } memcpy(p_dest, (((uint8_t *)p_src->block_id) + offset), size); app_notify(p_src, p_dest, PSTORAGE_LOAD_OP_CODE, size, NRF_SUCCESS); return NRF_SUCCESS; }
uint32_t pstorage_store(pstorage_handle_t * p_dest, uint8_t * p_src, pstorage_size_t size, pstorage_size_t offset) { VERIFY_MODULE_INITIALIZED(); NULL_PARAM_CHECK(p_src); NULL_PARAM_CHECK(p_dest); MODULE_ID_RANGE_CHECK(p_dest); BLOCK_ID_RANGE_CHECK(p_dest); SIZE_CHECK(p_dest, size); OFFSET_CHECK(p_dest, offset, size); // Verify word alignment. if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(uint32_t)offset))) { return NRF_ERROR_INVALID_ADDR; } uint32_t storage_addr = p_dest->block_id + offset; uint32_t retval = ble_flash_block_write((uint32_t *)storage_addr, (uint32_t *)p_src, (size /sizeof(uint32_t))); app_notify(p_dest, p_src, PSTORAGE_STORE_OP_CODE, size, retval); return retval; }
/**@brief Starts processing the queue if there are no pending flash operations * for which we are awaiting a callback. */ static ret_code_t queue_process(void) { ret_code_t ret = NRF_SUCCESS; /** If the queue is not being processed, and there are still * some elements in it, then start processing. */ if ( !(m_flags & FS_FLAG_PROCESSING) && (m_cmd_queue.count > 0)) { m_flags |= FS_FLAG_PROCESSING; ret = queue_process_impl(); /** There is ongoing flash-operation which was not * initiated by fstorage. */ if (ret == NRF_ERROR_BUSY) { // Wait for a system callback. m_flags |= FS_FLAG_FLASH_REQ_PENDING; // Stop processing the queue. m_flags &= ~FS_FLAG_PROCESSING; ret = NRF_SUCCESS; } else if (ret != NRF_SUCCESS) { // Another error has occurred. app_notify(ret, &m_cmd_queue.cmd[m_cmd_queue.rp]); } } // If we are already processing the queue, return immediately. return ret; }
/** * @brief Dequeues a command element. * * @retval NRF_SUCCESS on success, else an error code indicating reason for failure. */ static uint32_t cmd_queue_dequeue(void) { uint32_t retval; retval = NRF_SUCCESS; // If any flash operation is enqueued, schedule. if (m_cmd_queue.count > 0) { retval = cmd_process(); if (retval != NRF_SUCCESS) { // Flash could be accessed by modules other than Bond Manager, hence a busy error is // acceptable, but any other error needs to be indicated to the bond manager. if (retval != NRF_ERROR_BUSY) { app_notify(retval); } else { // In case of busy next trigger will be a success or a failure event. } } } else { // No flash access request pending. } return retval; }
/**@brief Flash operation failure callback handler. * * @details Function to keep track of retries and notify failures. */ static void on_operation_failure(fs_cmd_t const * const p_cmd) { if (++m_retry_count > FS_CMD_MAX_RETRIES) { app_notify(p_cmd, NRF_ERROR_TIMEOUT); queue_advance(); } }
/** * @brief Dequeues a command element. */ static uint32_t cmd_queue_dequeue(void) { uint32_t retval; cmd_queue_element_t * p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; // Update count and read pointer to process any queued requests if(m_round_val >= p_cmd->size) { //if we finished clearing data, tell our DFU master if(p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) { lumen_set_solid_rgb(255,255,255); lumen_leds_show(); //clear finished here finish_dfu_image_size_process(); } // Initialize/free the element as it is now processed. cmd_queue_init_element(m_cmd_queue.rp); m_round_val = 0; m_cmd_queue.count--; m_cmd_queue.rp++; } retval = NRF_SUCCESS; // If any flash operation is enqueued, schedule if (m_cmd_queue.count) { retval = process_cmd(); if (retval != NRF_SUCCESS) { // Flash could be accessed by modules other than Bond Manager, hence a busy error is // acceptable, but any other error needs to be indicated to the bond manager if (retval != NRF_ERROR_BUSY) { app_notify (retval); } else { // In case of busy next trigger will be a success or a failure event } } } else { // No flash access request pending } return retval; }
/** * @brief Handles Flash Access Result Events. */ void pstorage_sys_event_handler (uint32_t sys_evt) { uint32_t retval; retval = NRF_SUCCESS; // Its possible the flash access was not initiated by bond manager, hence // event is processed only if the event triggered was for an operation requested by the // bond manager. if (m_cmd_queue.flash_access == true) { cmd_queue_element_t * p_cmd; m_cmd_queue.flash_access = false; switch (sys_evt) { case NRF_EVT_FLASH_OPERATION_SUCCESS: p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; if ((p_cmd->op_code != PSTORAGE_CLEAR_OP_CODE) || (m_round_val >= p_cmd->size)) { app_notify(retval); } // Schedule any queued flash access operations retval = cmd_queue_dequeue (); if (retval != NRF_SUCCESS) { app_notify(retval); } break; case NRF_EVT_FLASH_OPERATION_ERROR: app_notify(NRF_ERROR_TIMEOUT); break; default: // No implementation needed. break; } } }
/**@brief Function to process the current element in the queue and return the result. * * @retval NRF_SUCCESS Success. * @retval NRF_ERROR_FORBIDDEN Error. Undefined command. * @retval Any error returned by the SoftDevice flash API. */ static ret_code_t queue_process(void) { ret_code_t ret; fs_cmd_t * const p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; if (m_cmd_queue.count > 0) { switch (p_cmd->op_code) { case FS_OP_STORE: ret = store_execute(p_cmd); break; case FS_OP_ERASE: ret = erase_execute(p_cmd); break; default: ret = NRF_ERROR_INTERNAL; break; } } else { ret = NRF_SUCCESS; } /** There is ongoing flash-operation which was not * initiated by fstorage. */ if (ret == NRF_ERROR_BUSY) { // Wait for a system callback. m_flags |= FS_FLAG_FLASH_REQ_PENDING; // Stop processing the queue. m_flags &= ~FS_FLAG_PROCESSING; ret = NRF_SUCCESS; } else if (ret != NRF_SUCCESS) { // Another error has occurred. app_notify(p_cmd, ret); } return ret; }
/**@brief Function to consume queue item and notify the return value of the operation. * * @details This function will report the result and remove the command from the queue after * notification. */ static void cmd_consume(uint32_t result, const fs_cmd_t * p_cmd) { // Consume the current item on the queue. uint8_t rp = m_cmd_queue.rp; m_cmd_queue.count--; if (m_cmd_queue.count == 0) { // There are no elements left. Stop processing the queue. m_flags &= ~FS_FLAG_PROCESSING; } if (++(m_cmd_queue.rp) == FS_CMD_QUEUE_SIZE) { m_cmd_queue.rp = 0; } // Notify upon successful operation. app_notify(result, p_cmd); // Reset the queue element. cmd_reset(rp); }
uint32_t pstorage_update(pstorage_handle_t * p_dest, uint8_t * p_src, pstorage_size_t size, pstorage_size_t offset) { uint32_t *p_swap_addr = (uint32_t *)PSTORAGE_SWAP_ADDR; uint32_t *p_page_addr1 = (uint32_t *) PAGE_BASE_ADDR(p_dest->block_id + offset); #ifdef SUPPORT_MODULES_LARGER_THAN_PAGE uint32_t *p_page_addr2 = (uint32_t *) PAGE_BASE_ADDR(p_dest->block_id + offset + size-1); #endif uint16_t head_word_count = offset/sizeof(uint32_t); uint16_t body_word_count = size/sizeof(uint32_t); uint16_t tail_word_count; uint32_t retval; VERIFY_MODULE_INITIALIZED(); NULL_PARAM_CHECK(p_src); NULL_PARAM_CHECK(p_dest); MODULE_ID_RANGE_CHECK(p_dest); BLOCK_ID_RANGE_CHECK(p_dest); SIZE_CHECK(p_dest, size); OFFSET_CHECK(p_dest, offset, size); // Verify word alignment. if ((!is_word_aligned(p_src)) || (!is_word_aligned((void *)(uint32_t)offset))) { return NRF_ERROR_INVALID_ADDR; } // erase swap page (void) ble_flash_page_erase(PSTORAGE_SWAP_ADDR / BLE_FLASH_PAGE_SIZE); #ifdef SUPPORT_MODULES_LARGER_THAN_PAGE if (p_page_addr1 == p_page_addr2) { #endif // tail is in the same page as head tail_word_count = BLE_FLASH_PAGE_SIZE/sizeof(uint32_t) - head_word_count - body_word_count; // copy of the head if (head_word_count) { retval = ble_flash_block_write(p_swap_addr, p_page_addr1, head_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } } // copy of the body retval = ble_flash_block_write(p_swap_addr+head_word_count, (uint32_t *)p_src, body_word_count); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // copy of the tail if (tail_word_count) { retval = ble_flash_block_write(p_swap_addr+head_word_count+body_word_count, p_page_addr1+head_word_count+body_word_count, tail_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } } // erase active page (void) ble_flash_page_erase((uint32_t)p_page_addr1 / BLE_FLASH_PAGE_SIZE); // restore updated page retval = ble_flash_block_write(p_page_addr1, p_swap_addr, BLE_FLASH_PAGE_SIZE/sizeof(uint32_t)); #ifdef SUPPORT_MODULES_LARGER_THAN_PAGE } else { // tail is in NOT in the same page as head - need to swap twice uint16_t body1_word_count = BLE_FLASH_PAGE_SIZE/sizeof(uint32_t) - head_word_count; uint16_t body2_word_count = body_word_count - body1_word_count; tail_word_count = BLE_FLASH_PAGE_SIZE/sizeof(uint32_t) - body2_word_count; // copy head retval = ble_flash_block_write(p_swap_addr, p_page_addr1, head_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // copy body1 retval = ble_flash_block_write(p_swap_addr, (uint32_t *)p_src, body1_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // erase active page (void) ble_flash_page_erase((uint32_t)p_page_addr1 / BLE_FLASH_PAGE_SIZE); // restore updated page1 retval = ble_flash_block_write(p_page_addr1, p_swap_addr, BLE_FLASH_PAGE_SIZE/sizeof(uint32_t)); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // erase swap page (void) ble_flash_page_erase(PSTORAGE_SWAP_ADDR / BLE_FLASH_PAGE_SIZE); // copy body2 retval = ble_flash_block_write(p_swap_addr, (uint32_t *)p_src + body1_word_count, body2_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // copy tail retval = ble_flash_block_write(p_swap_addr, p_page_addr2+body2_word_count, tail_word_count ); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } // erase active page (void) ble_flash_page_erase((uint32_t)p_page_addr2 / BLE_FLASH_PAGE_SIZE); // restore updated page2 retval = ble_flash_block_write(p_page_addr2, p_swap_addr, BLE_FLASH_PAGE_SIZE/sizeof(uint32_t)); if (retval != NRF_SUCCESS) { app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; } } #endif app_notify(p_dest, p_src, PSTORAGE_UPDATE_OP_CODE, size, retval); return retval; }
/** * @brief Handles Flash Access Result Events declared in pstorage_platform.h. * * @param[in] sys_evt System event to be handled. */ void pstorage_sys_event_handler(uint32_t sys_evt) { uint32_t retval = NRF_SUCCESS; // Its possible the flash access was not initiated by bond manager, hence // event is processed only if the event triggered was for an operation requested by the // bond manager. if (m_cmd_queue.flash_access == true) { cmd_queue_element_t * p_cmd; m_cmd_queue.flash_access = false; if (m_swap_state == STATE_SWAP_DIRTY) { if (sys_evt == NRF_EVT_FLASH_OPERATION_SUCCESS) { m_swap_state = STATE_INIT; } else { // If clearing the swap fails, set the application back to un-initialized, to give // the application a chance for a retry. m_module_initialized = false; } // Schedule any queued flash access operations. retval = cmd_queue_dequeue(); if (retval != NRF_SUCCESS) { app_notify(retval, &m_cmd_queue.cmd[m_cmd_queue.rp]); } return; } switch (sys_evt) { case NRF_EVT_FLASH_OPERATION_SUCCESS: { p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; m_round_val++; const bool store_finished = ((p_cmd->op_code == PSTORAGE_STORE_OP_CODE) && ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size)); const bool update_finished = ((p_cmd->op_code == PSTORAGE_UPDATE_OP_CODE) && (m_swap_state == STATE_COMPLETE)); const bool clear_block_finished = ((p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) && (m_swap_state == STATE_COMPLETE)); const bool clear_all_finished = ((p_cmd->op_code == PSTORAGE_CLEAR_OP_CODE) && ((m_round_val * SOC_MAX_WRITE_SIZE) >= p_cmd->size) && (m_swap_state == STATE_INIT)); if (update_finished || clear_block_finished || clear_all_finished || store_finished) { uint8_t queue_rp = m_cmd_queue.rp; m_swap_state = STATE_INIT; m_round_val = 0; m_cmd_queue.count--; m_cmd_queue.rp++; if (m_cmd_queue.rp >= PSTORAGE_CMD_QUEUE_SIZE) { m_cmd_queue.rp -= PSTORAGE_CMD_QUEUE_SIZE; } app_notify(retval, &m_cmd_queue.cmd[queue_rp]); // Initialize/free the element as it is now processed. cmd_queue_element_init(queue_rp); } // Schedule any queued flash access operations. retval = cmd_queue_dequeue(); if (retval != NRF_SUCCESS) { app_notify(retval, &m_cmd_queue.cmd[m_cmd_queue.rp]); } } break; case NRF_EVT_FLASH_OPERATION_ERROR: // Current command timed out and was not started in SoftDevice. p_cmd = &m_cmd_queue.cmd[m_cmd_queue.rp]; ASSERT(p_cmd->n_tries < SD_CMD_MAX_TRIES); if (++p_cmd->n_tries == SD_CMD_MAX_TRIES) { // If we have already attempted SD_CMD_MAX_TRIES times, give up. app_notify(NRF_ERROR_TIMEOUT, &m_cmd_queue.cmd[m_cmd_queue.rp]); } else { // Retry operation retval = cmd_process(); if (retval != NRF_SUCCESS && retval != NRF_ERROR_BUSY) { app_notify(retval, p_cmd); } } break; default: // No implementation needed. break; } } }