Simulated product dispenser

Dependencies:   HTS221

Fork of mbed-cloud-workshop-connect-HTS221 by Jim Carver

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_pal_blockdevice_implementation.c Source File

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)