Simulated product dispenser
Fork of mbed-cloud-workshop-connect-HTS221 by
arm_uc_pal_blockdevice_implementation.c
00001 //---------------------------------------------------------------------------- 00002 // The confidential and proprietary information contained in this file may 00003 // only be used by a person authorised under and to the extent permitted 00004 // by a subsisting licensing agreement from ARM Limited or its affiliates. 00005 // 00006 // (C) COPYRIGHT 2017 ARM Limited or its affiliates. 00007 // ALL RIGHTS RESERVED 00008 // 00009 // This entire notice must be reproduced on all copies of this file 00010 // and copies of this file may only be made by a person if such person is 00011 // permitted to do so under the terms of a subsisting license agreement 00012 // from ARM Limited or its affiliates. 00013 //---------------------------------------------------------------------------- 00014 00015 #if defined(ARM_UC_USE_PAL_BLOCKDEVICE) 00016 00017 #define __STDC_FORMAT_MACROS 00018 00019 #include "update-client-pal-blockdevice/arm_uc_pal_blockdevice.h" 00020 00021 #include "update-client-pal-blockdevice/arm_uc_pal_blockdevice_platform.h" 00022 00023 #include "update-client-common/arm_uc_config.h" 00024 #include "update-client-common/arm_uc_error.h" 00025 #include "update-client-common/arm_uc_types.h" 00026 #include "update-client-common/arm_uc_metadata_header_v2.h" 00027 00028 #define TRACE_GROUP "UCPI" 00029 #include "update-client-common/arm_uc_trace.h" 00030 #include <inttypes.h> 00031 00032 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS 00033 #define MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS 0 00034 #endif 00035 00036 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE 00037 #define MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE 0 00038 #endif 00039 00040 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE 00041 #define MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE 1 00042 #endif 00043 00044 #ifndef MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS 00045 #define MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS 1 00046 #endif 00047 00048 /* consistency check */ 00049 #if (MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE == 0) 00050 #error Update client storage page cannot be zero. 00051 #endif 00052 00053 #if (MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS == 0) 00054 #error Update client storage locations must be at least 1. 00055 #endif 00056 00057 /* Check that the statically allocated buffers are aligned with the block size */ 00058 #define ARM_UC_PAL_ONE_BUFFER (ARM_UC_BUFFER_SIZE / 2) 00059 #define ARM_UC_PAL_PAGES (ARM_UC_PAL_ONE_BUFFER / MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE) 00060 00061 #if !((ARM_UC_PAL_PAGES * MBED_CONF_UPDATE_CLIENT_STORAGE_PAGE) == ARM_UC_PAL_ONE_BUFFER) 00062 #error Update client buffer must be divisible by the block page size 00063 #endif 00064 00065 static ARM_UC_PAAL_UPDATE_SignalEvent_t pal_blockdevice_event_handler = NULL; 00066 static uint32_t pal_blockdevice_firmware_size = 0; 00067 static uint32_t pal_blockdevice_page_size = 0; 00068 static uint32_t pal_blockdevice_sector_size = 0; 00069 static uint32_t pal_blockdevice_hdr_size =0; 00070 00071 static void pal_blockdevice_signal_internal(uint32_t event) 00072 { 00073 if (pal_blockdevice_event_handler) 00074 { 00075 pal_blockdevice_event_handler(event); 00076 } 00077 } 00078 00079 /** 00080 * @brief Round size up to nearest page 00081 * 00082 * @param size The size that need to be rounded up 00083 * @return Returns the size rounded up to the nearest page 00084 */ 00085 static uint32_t pal_blockdevice_round_up_to_page(uint32_t size) 00086 { 00087 return ((size - 1)/pal_blockdevice_page_size + 1) * pal_blockdevice_page_size; 00088 } 00089 00090 /** 00091 * @brief Round size down to nearest page 00092 * 00093 * @param size The size that need to be rounded up 00094 * @return Returns the size rounded up to the nearest page 00095 */ 00096 static uint32_t pal_blockdevice_round_down_to_page(uint32_t size) 00097 { 00098 return (size/pal_blockdevice_page_size) * pal_blockdevice_page_size; 00099 } 00100 00101 /** 00102 * @brief Align size up to sector size 00103 * 00104 * @param size The size that need to be rounded up 00105 * @return Returns the size aligned to sector size 00106 */ 00107 static uint32_t pal_blockdevice_round_up_to_sector(uint32_t size) 00108 { 00109 return ((size - 1)/pal_blockdevice_sector_size + 1) * pal_blockdevice_sector_size; 00110 } 00111 00112 /** 00113 * @brief Align size down to sector size 00114 * 00115 * @param size The size that need to be rounded up 00116 * @return Returns the size aligned to sector boundary 00117 */ 00118 static uint32_t pal_blockdevice_round_down_to_sector(uint32_t size) 00119 { 00120 return (size / pal_blockdevice_sector_size) * pal_blockdevice_sector_size; 00121 } 00122 00123 /** 00124 * @brief Get the physicl slot address and size given slot_id 00125 * 00126 * @param slot_id Storage location ID. 00127 * @param slot_addr the slot address is returned in this pointer 00128 * @param slot_size the slot size is returned in this pointer 00129 * @return Returns ERR_NONE on success. 00130 * Returns ERR_INVALID_PARAMETER on error. 00131 */ 00132 static arm_uc_error_t pal_blockdevice_get_slot_addr_size(uint32_t slot_id, 00133 uint32_t* slot_addr, 00134 uint32_t* slot_size) 00135 { 00136 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00137 00138 if ((slot_id < MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS) && 00139 (slot_addr != NULL) && 00140 (slot_size != NULL)) 00141 { 00142 /* find the start address of the whole storage area. It needs to be aligned to 00143 sector boundary and we cannot go outside user defined storage area, hence 00144 rounding up to sector boundary */ 00145 uint32_t storage_start_addr = pal_blockdevice_round_up_to_sector( 00146 MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS); 00147 /* find the end address of the whole storage area. It needs to be aligned to 00148 sector boundary and we cannot go outside user defined storage area, hence 00149 rounding down to sector boundary */ 00150 uint32_t storage_end_addr = pal_blockdevice_round_down_to_sector( 00151 MBED_CONF_UPDATE_CLIENT_STORAGE_ADDRESS + \ 00152 MBED_CONF_UPDATE_CLIENT_STORAGE_SIZE); 00153 /* find the maximum size each slot can have given the start and end, without 00154 considering the alignment of individual slots */ 00155 uint32_t max_slot_size = (storage_end_addr - storage_start_addr) / \ 00156 MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS; 00157 /* find the start address of slot. It needs to align to sector boundary. We 00158 choose here to round down at each slot boundary */ 00159 *slot_addr = storage_start_addr + pal_blockdevice_round_down_to_sector( 00160 slot_id * max_slot_size); 00161 /* Rounding down slot size to sector boundary same as 00162 the slot start address so that we make sure two slot don't overlap */ 00163 *slot_size = pal_blockdevice_round_down_to_sector(max_slot_size); 00164 result.code = ERR_NONE; 00165 } 00166 00167 return result; 00168 } 00169 00170 /** 00171 * @brief Initialize the underlying storage and set the callback handler. 00172 * 00173 * @param callback Function pointer to event handler. 00174 * @return Returns ERR_NONE on accept, and signals the event handler with 00175 * either DONE or ERROR when complete. 00176 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00177 */ 00178 arm_uc_error_t ARM_UC_PAL_BlockDevice_Initialize(ARM_UC_PAAL_UPDATE_SignalEvent_t callback) 00179 { 00180 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00181 00182 if (callback) 00183 { 00184 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Initialize"); 00185 00186 int status = arm_uc_blockdevice_init(); 00187 pal_blockdevice_page_size = arm_uc_blockdevice_get_program_size(); 00188 pal_blockdevice_sector_size = arm_uc_blockdevice_get_erase_size(); 00189 pal_blockdevice_hdr_size = pal_blockdevice_round_up_to_page(ARM_UC_EXTERNAL_HEADER_SIZE_V2); 00190 00191 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00192 { 00193 pal_blockdevice_event_handler = callback; 00194 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_INITIALIZE_DONE); 00195 result.code = ERR_NONE; 00196 } 00197 } 00198 00199 return result; 00200 } 00201 00202 /** 00203 * @brief Get maximum number of supported storage locations. 00204 * 00205 * @return Number of storage locations. 00206 */ 00207 uint32_t ARM_UC_PAL_BlockDevice_GetMaxID(void) 00208 { 00209 return MBED_CONF_UPDATE_CLIENT_STORAGE_LOCATIONS; 00210 } 00211 00212 /** 00213 * @brief Prepare the storage layer for a new firmware image. 00214 * @details The storage location is set up to receive an image with 00215 * the details passed in the details struct. 00216 * 00217 * @param slot_id Storage location ID. 00218 * @param details Pointer to a struct with firmware details. 00219 * @param buffer Temporary buffer for formatting and storing metadata. 00220 * @return Returns ERR_NONE on accept, and signals the event handler with 00221 * either DONE or ERROR when complete. 00222 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00223 */ 00224 arm_uc_error_t ARM_UC_PAL_BlockDevice_Prepare(uint32_t slot_id, 00225 const arm_uc_firmware_details_t* details, 00226 arm_uc_buffer_t* buffer) 00227 { 00228 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00229 00230 if (details && buffer && buffer->ptr) 00231 { 00232 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Prepare: %" PRIX32 " %" PRIX32, 00233 slot_id, details->size); 00234 00235 /* encode firmware details in buffer */ 00236 arm_uc_error_t header_status = arm_uc_create_external_header_v2(details, 00237 buffer); 00238 if (header_status.error == ERR_NONE) 00239 { 00240 /* find the size needed to erase. Header is stored contiguous with firmware */ 00241 uint32_t erase_size = pal_blockdevice_round_up_to_sector(pal_blockdevice_hdr_size + \ 00242 details->size); 00243 00244 /* find address of slot */ 00245 uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00246 uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00247 result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size); 00248 00249 UC_PAAL_TRACE("erase: %" PRIX32 " %" PRIX32 " %" PRIX32, slot_addr, erase_size, slot_size); 00250 00251 int status = ARM_UC_BLOCKDEVICE_FAIL; 00252 if (result.error == ERR_NONE) 00253 { 00254 if (erase_size <= slot_size) 00255 { 00256 /* erase */ 00257 status = arm_uc_blockdevice_erase(slot_addr, erase_size); 00258 } 00259 else 00260 { 00261 UC_PAAL_ERR_MSG("not enough space for firmware image"); 00262 result.code = ERR_INVALID_PARAMETER; 00263 } 00264 } 00265 00266 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00267 { 00268 /* write header */ 00269 status = arm_uc_blockdevice_program(buffer->ptr, 00270 slot_addr, 00271 pal_blockdevice_hdr_size); 00272 00273 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00274 { 00275 /* set return code */ 00276 result.code = ERR_NONE; 00277 00278 /* store firmware size in global */ 00279 pal_blockdevice_firmware_size = details->size; 00280 00281 /* signal done */ 00282 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_PREPARE_DONE); 00283 } 00284 else 00285 { 00286 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed"); 00287 } 00288 } 00289 else 00290 { 00291 UC_PAAL_ERR_MSG("arm_uc_blockdevice_erase failed"); 00292 } 00293 } 00294 else 00295 { 00296 UC_PAAL_ERR_MSG("arm_uc_create_external_header_v2 failed"); 00297 } 00298 } 00299 00300 return result; 00301 } 00302 00303 /** 00304 * @brief Write a fragment to the indicated storage location. 00305 * @details The storage location must have been allocated using the Prepare 00306 * call. The call is expected to write the entire fragment before 00307 * signaling completion. 00308 * 00309 * @param slot_id Storage location ID. 00310 * @param offset Offset in bytes to where the fragment should be written. 00311 * @param buffer Pointer to buffer struct with fragment. 00312 * @return Returns ERR_NONE on accept, and signals the event handler with 00313 * either DONE or ERROR when complete. 00314 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00315 */ 00316 arm_uc_error_t ARM_UC_PAL_BlockDevice_Write(uint32_t slot_id, 00317 uint32_t offset, 00318 const arm_uc_buffer_t* buffer) 00319 { 00320 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00321 00322 if (buffer && buffer->ptr) 00323 { 00324 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Write: %" PRIX32 " %" PRIX32 " %" PRIX32, 00325 slot_id, offset, buffer->size); 00326 int status = ARM_UC_BLOCKDEVICE_SUCCESS; 00327 00328 /* find address of slot */ 00329 uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00330 uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00331 result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size); 00332 uint32_t physical_address = slot_addr + pal_blockdevice_hdr_size + offset; 00333 00334 /* check that we are not writing too much */ 00335 uint32_t aligned_size = 0; 00336 if (pal_blockdevice_firmware_size < offset + buffer->size) 00337 { 00338 UC_PAAL_ERR_MSG("programming more than firmware size %" PRIu32 00339 " < %" PRIu32 " + %" PRIu32, 00340 pal_blockdevice_firmware_size, offset, buffer->size); 00341 } 00342 else if ((pal_blockdevice_firmware_size > offset + buffer->size) && 00343 (buffer->size % pal_blockdevice_page_size != 0)) 00344 { 00345 UC_PAAL_ERR_MSG("program size %" PRIu32 " does not align to page size %" PRIu32, 00346 buffer->size, pal_blockdevice_page_size); 00347 } 00348 else if(pal_blockdevice_firmware_size == offset + buffer->size) 00349 { 00350 /* last chunk write page aligned data first */ 00351 aligned_size = pal_blockdevice_round_down_to_page(buffer->size); 00352 } 00353 else 00354 { 00355 aligned_size = buffer->size; 00356 } 00357 00358 /* aligned write */ 00359 if (result.error == ERR_NONE && aligned_size > 0) 00360 { 00361 status = arm_uc_blockdevice_program(buffer->ptr, 00362 physical_address, 00363 aligned_size); 00364 if (status == ARM_UC_BLOCKDEVICE_FAIL) 00365 { 00366 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed"); 00367 } 00368 } 00369 00370 /* last chunk write remainder */ 00371 uint32_t remainder_size = buffer->size - aligned_size; 00372 00373 if ((status == ARM_UC_BLOCKDEVICE_SUCCESS) && (remainder_size > 0)) 00374 { 00375 /* check if it is safe to use buffer, i.e. buffer is larger than a page */ 00376 if (buffer->size_max >= pal_blockdevice_page_size) 00377 { 00378 memmove(buffer->ptr, &(buffer->ptr[aligned_size]), remainder_size); 00379 status = arm_uc_blockdevice_program(buffer->ptr, 00380 physical_address + aligned_size, 00381 pal_blockdevice_page_size); 00382 } 00383 else 00384 { 00385 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed"); 00386 00387 status = ARM_UC_BLOCKDEVICE_FAIL; 00388 } 00389 } 00390 00391 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00392 { 00393 /* set return code */ 00394 result.code = ERR_NONE; 00395 00396 /* signal done */ 00397 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_WRITE_DONE); 00398 } 00399 else 00400 { 00401 UC_PAAL_ERR_MSG("arm_uc_blockdevice_program failed"); 00402 } 00403 } 00404 00405 return result; 00406 } 00407 00408 /** 00409 * @brief Close storage location for writing and flush pending data. 00410 * 00411 * @param slot_id Storage location ID. 00412 * @return Returns ERR_NONE on accept, and signals the event handler with 00413 * either DONE or ERROR when complete. 00414 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00415 */ 00416 arm_uc_error_t ARM_UC_PAL_BlockDevice_Finalize(uint32_t slot_id) 00417 { 00418 arm_uc_error_t result = { .code = ERR_NONE }; 00419 00420 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Finalize"); 00421 00422 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_FINALIZE_DONE); 00423 00424 return result; 00425 } 00426 00427 /** 00428 * @brief Read a fragment from the indicated storage location. 00429 * @details The function will read until the buffer is full or the end of 00430 * the storage location has been reached. The actual amount of 00431 * bytes read is set in the buffer struct. 00432 * 00433 * @param slot_id Storage location ID. 00434 * @param offset Offset in bytes to read from. 00435 * @param buffer Pointer to buffer struct to store fragment. buffer->size 00436 * contains the intended read size. 00437 * @return Returns ERR_NONE on accept, and signals the event handler with 00438 * either DONE or ERROR when complete. 00439 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00440 * buffer->size contains actual bytes read on return. 00441 */ 00442 arm_uc_error_t ARM_UC_PAL_BlockDevice_Read(uint32_t slot_id, 00443 uint32_t offset, 00444 arm_uc_buffer_t* buffer) 00445 { 00446 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00447 00448 if (buffer && buffer->ptr) 00449 { 00450 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Read: %" PRIX32 " %" PRIX32 " %" PRIX32, 00451 slot_id, offset, buffer->size); 00452 00453 /* find address of slot */ 00454 uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00455 uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00456 result = pal_blockdevice_get_slot_addr_size(slot_id, 00457 &slot_addr, 00458 &slot_size); 00459 uint32_t physical_address = slot_addr + pal_blockdevice_hdr_size + offset; 00460 uint32_t read_size = pal_blockdevice_round_up_to_page(buffer->size); 00461 int32_t status = ARM_UC_BLOCKDEVICE_FAIL; 00462 00463 if (read_size <= buffer->size_max) 00464 { 00465 status = arm_uc_blockdevice_read(buffer->ptr, 00466 physical_address, 00467 read_size); 00468 } 00469 00470 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00471 { 00472 /* set return code */ 00473 result.code = ERR_NONE; 00474 00475 /* signal done */ 00476 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_READ_DONE); 00477 } 00478 else 00479 { 00480 UC_PAAL_ERR_MSG("arm_uc_blockdevice_read failed"); 00481 } 00482 } 00483 00484 return result; 00485 } 00486 00487 /** 00488 * @brief Set the firmware image in the slot to be the new active image. 00489 * @details This call is responsible for initiating the process for 00490 * applying a new/different image. Depending on the platform this 00491 * could be: 00492 * * An empty call, if the installer can deduce which slot to 00493 * choose from based on the firmware details. 00494 * * Setting a flag to indicate which slot to use next. 00495 * * Decompressing/decrypting/installing the firmware image on 00496 * top of another. 00497 * 00498 * @param slot_id Storage location ID. 00499 * @return Returns ERR_NONE on accept, and signals the event handler with 00500 * either DONE or ERROR when complete. 00501 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00502 */ 00503 arm_uc_error_t ARM_UC_PAL_BlockDevice_Activate(uint32_t slot_id) 00504 { 00505 arm_uc_error_t result = { .code = ERR_NONE }; 00506 00507 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_Activate"); 00508 00509 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_ACTIVATE_DONE); 00510 00511 return result; 00512 } 00513 00514 /** 00515 * @brief Get firmware details for the firmware image in the slot passed. 00516 * @details This call populates the passed details struct with information 00517 * about the firmware image in the slot passed. Only the fields 00518 * marked as supported in the capabilities bitmap will have valid 00519 * values. 00520 * 00521 * @param slot_id Storage location ID. 00522 * @param details Pointer to firmware details struct to be populated. 00523 * @return Returns ERR_NONE on accept, and signals the event handler with 00524 * either DONE or ERROR when complete. 00525 * Returns ERR_INVALID_PARAMETER on reject, and no signal is sent. 00526 */ 00527 arm_uc_error_t ARM_UC_PAL_BlockDevice_GetFirmwareDetails( 00528 uint32_t slot_id, 00529 arm_uc_firmware_details_t* details) 00530 { 00531 arm_uc_error_t result = { .code = ERR_INVALID_PARAMETER }; 00532 00533 if (details) 00534 { 00535 UC_PAAL_TRACE("ARM_UC_PAL_BlockDevice_GetFirmwareDetails"); 00536 00537 /* find address of slot */ 00538 uint32_t slot_addr = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00539 uint32_t slot_size = ARM_UC_BLOCKDEVICE_INVALID_SIZE; 00540 result = pal_blockdevice_get_slot_addr_size(slot_id, &slot_addr, &slot_size); 00541 uint8_t buffer[pal_blockdevice_hdr_size]; 00542 00543 int status = arm_uc_blockdevice_read(buffer, 00544 slot_addr, 00545 pal_blockdevice_hdr_size); 00546 00547 if (status == ARM_UC_BLOCKDEVICE_SUCCESS) 00548 { 00549 result = arm_uc_parse_external_header_v2(buffer, details); 00550 00551 if (result.error == ERR_NONE) 00552 { 00553 /* signal done */ 00554 pal_blockdevice_signal_internal(ARM_UC_PAAL_EVENT_GET_FIRMWARE_DETAILS_DONE); 00555 } 00556 else 00557 { 00558 UC_PAAL_ERR_MSG("arm_uc_parse_external_header_v2 failed"); 00559 } 00560 } 00561 else 00562 { 00563 UC_PAAL_ERR_MSG("arm_uc_blockdevice_read failed"); 00564 } 00565 } 00566 00567 return result; 00568 } 00569 00570 #endif // #if defined(ARM_UC_USE_PAL_BLOCKDEVICE)
Generated on Tue Jul 12 2022 19:12:11 by 1.7.2