Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_source_manager.c Source File

arm_uc_source_manager.c

00001 // ----------------------------------------------------------------------------
00002 // Copyright 2016-2017 ARM Ltd.
00003 //
00004 // SPDX-License-Identifier: Apache-2.0
00005 //
00006 // Licensed under the Apache License, Version 2.0 (the "License");
00007 // you may not use this file except in compliance with the License.
00008 // You may obtain a copy of the License at
00009 //
00010 //     http://www.apache.org/licenses/LICENSE-2.0
00011 //
00012 // Unless required by applicable law or agreed to in writing, software
00013 // distributed under the License is distributed on an "AS IS" BASIS,
00014 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00015 // See the License for the specific language governing permissions and
00016 // limitations under the License.
00017 // ----------------------------------------------------------------------------
00018 
00019 #include "update-client-source-manager/arm_uc_source_manager.h"
00020 
00021 #include "update-client-common/arm_uc_common.h"
00022 #include "update-client-common/arm_uc_config.h"
00023 #include "update-client-source/arm_uc_source.h"
00024 
00025 #if defined(ARM_UC_PROFILE_MBED_CLOUD_CLIENT) && (ARM_UC_PROFILE_MBED_CLOUD_CLIENT == 1)
00026 #include "pal.h"
00027 #endif
00028 
00029 #include <stdint.h>
00030 #include <stdlib.h>
00031 
00032 // TRACE.
00033 // ------
00034 
00035 // to disable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT(...)
00036 // or to enable extra trace, uncomment UC_SRCE_TRACE_ENTRY/VERBOSE/EXIT UC_SRCE_TRACE
00037 #define UC_SRCE_TRACE_ENTRY(...)
00038 #define UC_SRCE_TRACE_VERBOSE(...)
00039 #define UC_SRCE_TRACE_EXIT(...)
00040 //#define UC_SRCE_TRACE_ENTRY UC_SRCE_TRACE
00041 //#define UC_SRCE_TRACE_VERBOSE UC_SRCE_TRACE
00042 //#define UC_SRCE_TRACE_EXIT UC_SRCE_TRACE
00043 
00044 // DATA & CONFIG.
00045 // --------------
00046 
00047 static const ARM_UPDATE_SOURCE *source_registry[MAX_SOURCES];
00048 static ARM_SOURCE_SignalEvent_t event_cb;
00049 
00050 // storage set aside for adding event_cb to the event queue
00051 static arm_uc_callback_t event_cb_storage = {0};
00052 
00053 static arm_uc_error_t ucsm_last_error = {ERR_NONE};
00054 
00055 typedef enum {
00056     QUERY_TYPE_UNKNOWN,
00057     QUERY_TYPE_MANIFEST_DEFAULT,
00058     QUERY_TYPE_MANIFEST_URL,
00059     QUERY_TYPE_FIRMWARE,
00060     QUERY_TYPE_KEYTABLE
00061 } query_type_t;
00062 
00063 typedef struct {
00064     arm_uc_uri_t *uri;             // the uri from which the resourced should be fetched
00065     arm_uc_buffer_t *buffer;       // buffer given by caller to contain the results of the fetch
00066     uint32_t offset;               // offset parameter passed to the source
00067     query_type_t type;             // type of request, whether is manifest, firmware or keytable
00068     uint8_t excludes[MAX_SOURCES]; // records the tried and failed sources during a get request
00069     uint8_t current_source;        // records the index of source use in the get request in progress
00070 } request_t;
00071 
00072 // Hold information about the request in flight, there will always only be one request in flight
00073 static request_t request_in_flight;
00074 
00075 // FORWARD DECLARATIONS.
00076 // ---------------------
00077 
00078 /**
00079  * @brief Initialise a request_t struct, called when a new request
00080  *        have been initiated from the hub
00081  */
00082 static arm_uc_error_t ARM_UCSM_RequestStructInit(request_t *request);
00083 
00084 /**
00085  * @brief The SourceRegistry is an array of ARM_UPDATE_SOURCE
00086  */
00087 static arm_uc_error_t ARM_UCSM_SourceRegistryInit(void);
00088 static arm_uc_error_t ARM_UCSM_SourceRegistryAdd(const ARM_UPDATE_SOURCE *source);
00089 static arm_uc_error_t ARM_UCSM_SourceRegistryRemove(const ARM_UPDATE_SOURCE *source);
00090 
00091 /**
00092  * @brief return the index of the source with the smallest cost
00093  * @param url Struct containing URL. NULL for default Manifest.
00094  * @param type The type of current request.
00095  * @param excludes Pointer to an array of size MAX_SOURCES to indicate
00096  *                 sources we want to exclude in the search. Set excludes[i]=1 to
00097  *                 exclude source_registry[i]
00098  * @param index Used to return the index of the source with the smallest cost
00099  */
00100 static arm_uc_error_t ARM_UCSM_SourceRegistryGetLowestCost(arm_uc_uri_t *uri,
00101                                                            query_type_t type,
00102                                                            uint8_t *excludes,
00103                                                            uint32_t *index);
00104 
00105 /**
00106  * @brief Find the source of lowest cost and call the corresponding method
00107  *        depending on the type of the request. Retry with source of the next
00108  *        smallest cost if previous sources failed until the source registry
00109  *        is exhausted.
00110  */
00111 static arm_uc_error_t ARM_UCSM_Get(request_t *req);
00112 
00113 /**
00114  * @brief Catch callbacks from sources to enable error handling
00115  */
00116 static void ARM_UCSM_CallbackWrapper(uintptr_t event);
00117 
00118 #if defined(ARM_UC_PROFILE_MBED_CLOUD_CLIENT) && (ARM_UC_PROFILE_MBED_CLOUD_CLIENT == 1)
00119 
00120 static void ARM_UCSM_ScheduleAsyncBusyRetryGet(void);
00121 
00122 // BUSY RETRY.
00123 // -----------
00124 
00125 // structs to enable timer callback.
00126 static palTimerID_t async_retry_timer_id;
00127 static arm_uc_callback_t async_retry_timer_callback_struct;
00128 
00129 // default settings for retries.
00130 #define BUSY_RETRY_DELAY_MS         500
00131 #define MAX_BUSY_RETRIES            2
00132 
00133 // number of retries that have taken place.
00134 static uint32_t num_busy_retries = 0;
00135 
00136 /**
00137  * @brief Retry Get if source was busy at previous attempt.
00138  * @details If source is busy ARM_UCSM_AsyncRetryGet is registered with event queue,
00139  *        so it is called again to retry same source. RetryGet is delayed a bit
00140  *        to allow the link to clear (if possible), and is retried multiple times,
00141  *        with a gap between tries.
00142  */
00143 static void ARM_UCSM_DoAsyncBusyRetryGet(uintptr_t unused)
00144 {
00145     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00146     (void) unused;
00147 
00148     // if already retried as many times as allowed, bail out with error,
00149     //  otherwise try the Get, and schedule follow-up if fails.
00150     if (++num_busy_retries > MAX_BUSY_RETRIES) {
00151         num_busy_retries = 0;
00152         ARM_UCSM_RequestStructInit(&request_in_flight);
00153         // TODO this potentially aborts the whole download if on an intermediate fragment,
00154         //        so figure out if that is the desired behaviour or not.
00155         //      the resume engine is *really* only designed to protect a single fragment,
00156         //        but this seems way too fragile to have around.
00157         ARM_UCSM_SetError(ARM_UC_ERROR(SOMA_ERR_UNSPECIFIED));
00158         ARM_UC_PostCallback(&event_cb_storage, event_cb, ARM_UC_SM_EVENT_ERROR);
00159     } else if (ARM_UCSM_Get(&request_in_flight).error != ERR_NONE) {
00160         ARM_UCSM_ScheduleAsyncBusyRetryGet();
00161     }
00162     UC_SRCE_TRACE_EXIT(".. %s", __func__);
00163 }
00164 
00165 /**
00166  * @brief post scheduled action to event queue to avoid running in timer context.
00167  */
00168 static void ARM_UCSM_PostAsyncBusyRetryGet(
00169     void const *unused)
00170 {
00171     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00172     (void)unused;
00173 
00174     pal_osTimerStop(async_retry_timer_id);
00175     ARM_UC_PostCallback(&async_retry_timer_callback_struct, ARM_UCSM_DoAsyncBusyRetryGet, 0);
00176 }
00177 
00178 /**
00179  * @brief request timer-scheduled invocation, invokes post directly if timer fails.
00180  */
00181 static void ARM_UCSM_ScheduleAsyncBusyRetryGet(void)
00182 {
00183     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00184     palStatus_t pal_status = PAL_SUCCESS;
00185     // if delay timer has not already been initialized then do so.
00186     if (async_retry_timer_id == 0) {
00187         pal_status = pal_osTimerCreate(
00188                          ARM_UCSM_PostAsyncBusyRetryGet, NULL, palOsTimerOnce , &async_retry_timer_id);
00189     }
00190     // if timer has been successfully initialized then install delayed action.
00191     if (pal_status == PAL_SUCCESS) {
00192         pal_status = pal_osTimerStart(async_retry_timer_id, BUSY_RETRY_DELAY_MS);
00193     }
00194     // if not successfully installed delayed action then post action directly.
00195     if (pal_status != PAL_SUCCESS) {
00196         async_retry_timer_id = 0;
00197         ARM_UC_PostCallback(&async_retry_timer_callback_struct, ARM_UCSM_DoAsyncBusyRetryGet, 0);
00198     }
00199 }
00200 
00201 #else // ARM_UC_PROFILE_MBED_CLOUD_CLIENT
00202 
00203 /**
00204  * @brief Retry get due to source being busy
00205  */
00206 static void ARM_UCSM_AsyncRetryGet(uintptr_t);
00207 
00208 #endif // ARM_UC_PROFILE_MBED_CLOUD_CLIENT
00209 
00210 // UTILITY.
00211 // --------
00212 
00213 /**
00214  * @brief Initialise the `Request` struct
00215  */
00216 static arm_uc_error_t ARM_UCSM_RequestStructInit(request_t *request)
00217 {
00218     memset(request, 0, sizeof(request_t));
00219     request->current_source = MAX_SOURCES;
00220     request->type           = QUERY_TYPE_UNKNOWN;
00221 
00222     return (arm_uc_error_t) { ERR_NONE };
00223 }
00224 
00225 
00226 // REGISTRY.
00227 // ---------
00228 
00229 /**
00230  * @brief Initialise the source_registry array to NULL
00231  */
00232 static arm_uc_error_t ARM_UCSM_SourceRegistryInit(void)
00233 {
00234     memset(source_registry, 0, sizeof(source_registry));
00235     return (arm_uc_error_t) { ERR_NONE };
00236 }
00237 /**
00238  * @brief Returns the index of the given address (possibly NULL) in the source array,
00239  *        or MAX_SOURCES if the address is not found.
00240  */
00241 static uint32_t ARM_UCSM_GetIndexOf(const ARM_UPDATE_SOURCE *source)
00242 {
00243     uint32_t index = MAX_SOURCES;
00244     for (uint32_t i = 0; i < MAX_SOURCES; i++) {
00245         if (source_registry[i] == source) {
00246             index = i;
00247             break;
00248         }
00249     }
00250     return index;
00251 }
00252 /**
00253  * @brief Returns the index of the given source address in the source array,
00254  *        or MAX_SOURCES if the source is not found.
00255  */
00256 static uint32_t ARM_UCSM_GetIndexOfSource(const ARM_UPDATE_SOURCE *source)
00257 {
00258     return ARM_UCSM_GetIndexOf(source);
00259 }
00260 
00261 /**
00262  * @brief Add pointer to source to the source_registry array
00263  */
00264 static arm_uc_error_t ARM_UCSM_SourceRegistryAdd(const ARM_UPDATE_SOURCE *source)
00265 {
00266     uint32_t index = ARM_UCSM_GetIndexOf(NULL);
00267     if (index == MAX_SOURCES) {
00268         return ARM_UCSM_SetError((arm_uc_error_t) { SOMA_ERR_SOURCE_REGISTRY_FULL });
00269     } else {
00270         source_registry[index] = source;
00271         return (arm_uc_error_t) { ERR_NONE };
00272     }
00273 
00274 }
00275 
00276 /**
00277  * @brief Remove pointer to source from the source_registry array
00278  */
00279 static arm_uc_error_t ARM_UCSM_SourceRegistryRemove(const ARM_UPDATE_SOURCE *source)
00280 {
00281     uint32_t index = ARM_UCSM_GetIndexOfSource(source);
00282 
00283     if (index == MAX_SOURCES) { // source not found
00284         return ARM_UCSM_SetError((arm_uc_error_t) { SOMA_ERR_SOURCE_NOT_FOUND });
00285     }
00286 
00287     source_registry[index] = NULL;
00288     return (arm_uc_error_t) { ERR_NONE };
00289 }
00290 
00291 // SOURCE MANAGEMENT.
00292 // ------------------
00293 
00294 /**
00295  * @brief find the index of the source with the smallest cost
00296  * @param url Struct containing URL. NULL for default Manifest.
00297  * @param type The type of current request.
00298  * @param excludes Pointer to an array of size MAX_SOURCES to indicate
00299  *                 sources we want to exclude in the search. Set excludes[i]=1 to
00300  *                 exclude source_registry[i]
00301  * @param index Used to return the index of the source with the smalllest cost
00302  * @return error status.
00303  */
00304 static arm_uc_error_t ARM_UCSM_SourceRegistryGetLowestCost(arm_uc_uri_t *uri,
00305                                                            query_type_t type,
00306                                                            uint8_t *excludes,
00307                                                            uint32_t *index)
00308 {
00309     uint32_t min_cost = UINT32_MAX;
00310     uint32_t min_cost_index = 0;
00311 
00312     // start with no route found, could be no sources are registered, skips loop.
00313     arm_uc_error_t retval = (arm_uc_error_t) { SOMA_ERR_NO_ROUTE_TO_SOURCE };
00314 
00315     UC_SRCE_TRACE_ENTRY(">> %s, type %" PRIu32, __func__, (uint32_t)type);
00316 
00317     // loop through all sources
00318     for (uint32_t i = 0; i < MAX_SOURCES; i++) {
00319         // assume no route to begin each loop, need to actually find one to change this.
00320         retval = (arm_uc_error_t) { SOMA_ERR_NO_ROUTE_TO_SOURCE };
00321 
00322         // if source is NULL or it has been explicitly excluded because of failure before
00323         if (source_registry[i] == NULL) {
00324             continue;
00325         } else if ((excludes != NULL) && (excludes[i] == 1)) {
00326             UC_SRCE_TRACE_VERBOSE("skipping excluded index %" PRIu32, i);
00327             continue;
00328         } else {
00329             UC_SRCE_TRACE_VERBOSE("testing index %" PRIu32, i);
00330         }
00331 
00332         ARM_SOURCE_CAPABILITIES cap  = source_registry[i]->GetCapabilities();
00333         uint32_t cost = UINT32_MAX;
00334 
00335         switch (type) {
00336             case QUERY_TYPE_UNKNOWN:
00337                 break;
00338             case QUERY_TYPE_MANIFEST_DEFAULT:
00339                 if ((uri == NULL) && (cap.manifest_default == 1)) {
00340                     UC_SRCE_TRACE_VERBOSE("getting manifest default cost, index %" PRIu32, i);
00341                     retval = source_registry[i]->GetManifestDefaultCost(&cost);
00342                 }
00343                 break;
00344             case QUERY_TYPE_MANIFEST_URL:
00345                 if ((uri != NULL) && (cap.manifest_url == 1)) {
00346                     UC_SRCE_TRACE_VERBOSE("getting manifest url cost, index %" PRIu32, i);
00347                     retval = source_registry[i]->GetManifestURLCost(uri, &cost);
00348                 }
00349                 break;
00350             case QUERY_TYPE_FIRMWARE:
00351                 if ((uri != NULL) && (cap.firmware == 1)) {
00352                     UC_SRCE_TRACE_VERBOSE("getting firmware url cost, index %" PRIu32, i);
00353                     retval = source_registry[i]->GetFirmwareURLCost(uri, &cost);
00354                 }
00355                 break;
00356             case QUERY_TYPE_KEYTABLE:
00357                 if ((uri != NULL) && (cap.keytable == 1)) {
00358                     UC_SRCE_TRACE_VERBOSE("getting keytable url cost, index %" PRIu32, i);
00359                     retval = source_registry[i]->GetKeytableURLCost(uri, &cost);
00360                 }
00361                 break;
00362             default:
00363                 break;
00364         }
00365         if (retval.error != ERR_NONE) {
00366             // get cost from source i failed, either no match or during assessment.
00367             // cost is invalid at this point, so skip to next iteration
00368             ARM_UCSM_SetError(retval);
00369             UC_SRCE_TRACE("invalid cost for index %" PRIu32 " type %" PRIu32, i, (uint32_t)type);
00370             continue;
00371         }
00372         // record the cost and i if cost is lower than stored minimum cost.
00373         if (cost < min_cost) {
00374             min_cost = cost;
00375             min_cost_index = i;
00376         }
00377     }
00378     // if no minimum cost was found, then no route was found.
00379     // otherwise return the best route available.
00380     if (min_cost == UINT32_MAX) {
00381         UC_SRCE_ERR_MSG(".. %s: Error - No route", __func__);
00382         return ARM_UCSM_SetError((arm_uc_error_t) { SOMA_ERR_NO_ROUTE_TO_SOURCE });
00383     } else {
00384         *index = min_cost_index;
00385         UC_SRCE_TRACE_VERBOSE("%s index = %" PRIu32, __func__, min_cost_index);
00386         return (arm_uc_error_t) { ERR_NONE };
00387     }
00388 }
00389 
00390 #if defined(ARM_UC_PROFILE_MBED_CLOUD_CLIENT) && (ARM_UC_PROFILE_MBED_CLOUD_CLIENT == 1)
00391 
00392 /**
00393  * @brief find source of lowest cost and call consecutive sources until retrieved.
00394  * @details Find the source of lowest cost and call the corresponding method
00395  *        depending on the type of the request. Retry with source of the next
00396  *        smallest cost if previous sources failed until the source registry
00397  *        is exhausted.
00398  * @param pointer to struct containing details of requested info.
00399  * @return error status
00400  */
00401 static arm_uc_error_t ARM_UCSM_Get(request_t *req)
00402 {
00403     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00404     arm_uc_error_t retval = (arm_uc_error_t) { ERR_NONE };
00405 
00406     if (req->uri != NULL) {
00407         UC_SRCE_TRACE_VERBOSE("    with %" PRIxPTR ", host [%s], path [%s], type %" PRIu32,
00408                               (uintptr_t)req->uri, req->uri->host, req->uri->path, (uint32_t)req->type);
00409     } else {
00410         UC_SRCE_TRACE_VERBOSE("    with NULL, type %" PRIu32, (uint32_t)req->type);
00411     }
00412 
00413     uint32_t index = 0;
00414     if (retval.error == ERR_NONE) {
00415         // get the source of lowest cost, checking that call is valid.
00416         retval = ARM_UCSM_SourceRegistryGetLowestCost(
00417                      req->uri,
00418                      req->type,
00419                      req->excludes,
00420                      &index);
00421     }
00422     if (retval.error == ERR_NONE) {
00423         // call is known to be valid, no need to check URI again.
00424         switch (req->type) {
00425             case QUERY_TYPE_MANIFEST_DEFAULT:
00426                 UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetManifestDefault", index);
00427                 retval = source_registry[index]->GetManifestDefault(req->buffer, req->offset);
00428                 break;
00429             case QUERY_TYPE_MANIFEST_URL:
00430                 UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetManifestURL with %" PRIxPTR, index, (uintptr_t)req->uri);
00431                 retval = source_registry[index]->GetManifestURL(req->uri, req->buffer, req->offset);
00432                 break;
00433             case QUERY_TYPE_FIRMWARE:
00434                 UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetFirmwareFragment with %" PRIxPTR, index, (uintptr_t)req->uri);
00435                 retval = source_registry[index]->GetFirmwareFragment(req->uri, req->buffer, req->offset);
00436                 break;
00437             case QUERY_TYPE_KEYTABLE:
00438                 UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetKeytableURL with %" PRIxPTR, index, (uintptr_t)req->uri);
00439                 retval = source_registry[index]->GetKeytableURL(req->uri, req->buffer);
00440                 break;
00441             default:
00442                 if (req->uri == NULL) {
00443                     UC_SRCE_ERR_MSG("-ARM_UCSM_Get: Error - Invalid parameter (URI == NULL)");
00444                     ARM_UCSM_SetError(retval = (arm_uc_error_t) { SOMA_ERR_INVALID_URI });
00445                 } else {
00446                     UC_SRCE_ERR_MSG("-ARM_UCSM_Get: Error - Invalid parameter (unknown request type)");
00447                     ARM_UCSM_SetError(retval = (arm_uc_error_t) { SOMA_ERR_INVALID_REQUEST });
00448                 }
00449                 break;
00450         }
00451     } else {
00452         UC_SRCE_ERR_MSG("%s error retval.code %" PRIu32, __func__, retval.code);
00453     }
00454 
00455     // decide what to do based on the results of preceding efforts.
00456     if (retval.code == SRCE_ERR_BUSY) {
00457         UC_SRCE_TRACE_VERBOSE("%s Busy -> ScheduleAsyncBusyRetryGet", __func__);
00458         ARM_UCSM_ScheduleAsyncBusyRetryGet();
00459         retval = (arm_uc_error_t) { ERR_NONE };
00460     } else if (retval.code == SOMA_ERR_NO_ROUTE_TO_SOURCE) {
00461         UC_SRCE_ERR_MSG(".. %s: Error - no route available", __func__);
00462         return retval;
00463     } else if (retval.error != ERR_NONE) {
00464         // failure, try source with the next smallest cost.
00465         ARM_UCSM_SetError(retval);
00466         req->excludes[index] = 1;
00467         UC_SRCE_TRACE_VERBOSE(".. %s: Error - failure (try source with the next smallest cost)", __func__);
00468         retval = ARM_UCSM_Get(req);
00469     } else {
00470         // record the index of source handling the get request currently.
00471         req->current_source = index;
00472         UC_SRCE_TRACE_VERBOSE(".. %s: Using source %" PRIu32, __func__, index);
00473         retval = (arm_uc_error_t) { ERR_NONE };
00474     }
00475     if (ARM_UC_IS_ERROR(retval)) {
00476         ARM_UCSM_SetError(retval);
00477     }
00478     return retval;
00479 }
00480 
00481 #else // ARM_UC_PROFILE_MBED_CLOUD_CLIENT
00482 
00483 /**
00484  * @brief Find the source of lowest cost and call the corresponding method
00485  *        depending on the type of the request. Retry with source of the next
00486  *        smallest cost if previous sources failed until the source registry
00487  *        is exhausted.
00488  */
00489 static arm_uc_error_t ARM_UCSM_Get(request_t *req)
00490 {
00491     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00492     if (req->uri != NULL) {
00493         UC_SRCE_TRACE_VERBOSE("    with %s" PRIx32 ", host [%s], path [%s], type %" PRId16,
00494                               req->uri->ptr, req->uri->host, req->uri->path, req->type);
00495     } else {
00496         UC_SRCE_TRACE_VERBOSE("    with NULL, type %" PRId16, req->type);
00497     }
00498     uint32_t index = 0;
00499 
00500     ARM_UC_INIT_ERROR(retval, ERR_NONE);
00501 
00502     // get the source of lowest cost
00503     retval = ARM_UCSM_SourceRegistryGetLowestCost(req->uri, req->type, req->excludes, &index);
00504     if (ARM_UC_IS_ERROR(retval)) {
00505         UC_SRCE_ERR_MSG(".. %s: error retval.code %" PRIx32, __func__, retval.code);
00506         return ARM_UCSM_SetError(retval);
00507     }
00508 
00509     if ((req->uri == NULL) && (req->type == QUERY_TYPE_MANIFEST_DEFAULT)) {
00510         UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetManifestDefault", index);
00511         retval = source_registry[index]->GetManifestDefault(req->buffer, req->offset);
00512     } else if ((req->uri != NULL) && (req->type == QUERY_TYPE_MANIFEST_URL)) {
00513         UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetManifestURL with %s", index, req->uri->ptr);
00514         retval = source_registry[index]->GetManifestURL(req->uri, req->buffer, req->offset);
00515     } else if ((req->uri != NULL) && (req->type == QUERY_TYPE_FIRMWARE)) {
00516         UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetFirmwareFragment with %s", index, req->uri->ptr);
00517         retval = source_registry[index]->GetFirmwareFragment(req->uri, req->buffer, req->offset);
00518     } else if ((req->uri != NULL) && (req->type == QUERY_TYPE_KEYTABLE)) {
00519         UC_SRCE_TRACE_VERBOSE("calling source %" PRIu32 " GetKeytableURL with %s", index, req->uri->ptr);
00520         retval = source_registry[index]->GetKeytableURL(req->uri, req->buffer);
00521     } else {
00522         if (req->uri == NULL) {
00523             UC_SRCE_TRACE(".. %s: Error - Invalid parameter (URI == NULL)", __func__);
00524             return ARM_UCSM_SetError(ARM_UC_ERROR(SOMA_ERR_INVALID_URI));
00525         } else {
00526             UC_SRCE_TRACE("..%s: Error - Invalid parameter (unknown request type)", __func__);
00527             return ARM_UCSM_SetError(ARM_UC_ERROR(SOMA_ERR_INVALID_REQUEST));
00528         }
00529     }
00530     if (ARM_UC_ERROR_MATCHES(retval, SRCE_ERR_BUSY)) {
00531         UC_SRCE_TRACE(".. %s: Error - Busy -> PostCallback AsyncRetryGet", __func__);
00532         ARM_UC_PostCallback(&event_cb_storage, ARM_UCSM_AsyncRetryGet, 0);
00533         return ARM_UC_ERROR(ERR_NONE);
00534     } else if (ARM_UC_IS_ERROR(retval)) {
00535         // failure, try source with the next smallest cost
00536         ARM_UCSM_SetError(retval);
00537         req->excludes[index] = 1;
00538         UC_SRCE_TRACE(".. %s: Error - failure (try source with the next smallest cost)", __func__);
00539         return ARM_UCSM_Get(req);
00540     }
00541     // record the index of source handling the get request currently
00542     req->current_source = index;
00543     UC_SRCE_TRACE_EXIT(".. %s, using source %" PRIu32, __func__, index);
00544 
00545     return ARM_UC_ERROR(ERR_NONE);
00546 }
00547 
00548 /**
00549  * @brief If source is busy ARM_UCSM_AsyncRetryGet is registered with
00550           the event queue so it is called again to retry the same source
00551  */
00552 static void ARM_UCSM_AsyncRetryGet(uintptr_t unused)
00553 {
00554     (void) unused;
00555 
00556     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00557     arm_uc_error_t retval = ARM_UCSM_Get(&request_in_flight);
00558     if (retval.error != ERR_NONE) {
00559         ARM_UCSM_RequestStructInit(&request_in_flight);
00560         ARM_UCSM_SetError(retval);
00561         ARM_UC_PostCallback(&event_cb_storage, event_cb, ARM_UC_SM_EVENT_ERROR);
00562     }
00563     UC_SRCE_TRACE_EXIT(".. %s", __func__);
00564 }
00565 
00566 #endif // ARM_UC_PROFILE_MBED_CLOUD_CLIENT
00567 
00568 /**
00569  * @brief Translate source event into source manager event
00570  */
00571 static ARM_UC_SM_Event_t ARM_UCSM_TranslateEvent(uintptr_t source_event)
00572 {
00573     ARM_UC_SM_Event_t event = ARM_UC_SM_EVENT_ERROR;
00574 
00575     switch (source_event) {
00576         case EVENT_NOTIFICATION:
00577             event = ARM_UC_SM_EVENT_NOTIFICATION;
00578             break;
00579         case EVENT_MANIFEST:
00580             event = ARM_UC_SM_EVENT_MANIFEST;
00581             break;
00582         case EVENT_FIRMWARE:
00583             event = ARM_UC_SM_EVENT_FIRMWARE;
00584             break;
00585         case EVENT_KEYTABLE:
00586             event = ARM_UC_SM_EVENT_KEYTABLE;
00587             break;
00588         case EVENT_ERROR:
00589             event = ARM_UC_SM_EVENT_ERROR;
00590             break;
00591         case EVENT_ERROR_SOURCE:
00592             event = ARM_UC_SM_EVENT_ERROR_SOURCE;
00593             break;
00594         case EVENT_ERROR_BUFFER_SIZE:
00595             event = ARM_UC_SM_EVENT_ERROR_BUFFER_SIZE;
00596             break;
00597     }
00598 
00599     return event;
00600 }
00601 
00602 /**
00603  * @brief Catch callbacks from sources to enable error handling
00604  */
00605 static void ARM_UCSM_CallbackWrapper(uintptr_t source_event)
00606 {
00607     UC_SRCE_TRACE_ENTRY(">> %s", __func__);
00608     UC_SRCE_TRACE_VERBOSE("source_event == %" PRIu32, source_event);
00609     ARM_UC_SM_Event_t event = ARM_UCSM_TranslateEvent(source_event);
00610 
00611     if ((event == ARM_UC_SM_EVENT_ERROR)
00612             && (request_in_flight.type != QUERY_TYPE_UNKNOWN)) {
00613         UC_SRCE_TRACE("ARM_UCSM_TranslateEvent event error == %" PRId16, event);
00614         request_in_flight.excludes[request_in_flight.current_source] = 1;
00615         arm_uc_error_t retval = ARM_UCSM_Get(&request_in_flight);
00616         if (retval.code != ERR_NONE) {
00617             UC_SRCE_TRACE("ARM_UCSM_Get() retval.code == %" PRIx32, retval.code);
00618             ARM_UCSM_RequestStructInit(&request_in_flight);
00619             ARM_UCSM_SetError(retval);
00620             ARM_UC_PostCallback(&event_cb_storage, event_cb, event);
00621         }
00622     } else {
00623         ARM_UCSM_RequestStructInit(&request_in_flight);
00624         ARM_UC_PostCallback(&event_cb_storage, event_cb, event);
00625     }
00626     UC_SRCE_TRACE_EXIT(".. %s", __func__);
00627 }
00628 
00629 // PUBLIC API.
00630 // -----------
00631 
00632 /* further documentation of the API can be found in source_manager.h */
00633 
00634 arm_uc_error_t ARM_UCSM_Initialize(ARM_SOURCE_SignalEvent_t callback)
00635 {
00636     // remember the callback
00637     event_cb = callback;
00638 
00639     // init source_registry to NULL
00640     return ARM_UCSM_SourceRegistryInit();
00641 }
00642 
00643 arm_uc_error_t ARM_UCSM_Uninitialize()
00644 {
00645     for (size_t i = 0; i < MAX_SOURCES; i++) {
00646         if (source_registry[i] != NULL) {
00647             source_registry[i]->Uninitialize();
00648             source_registry[i] = NULL;
00649         }
00650     }
00651     return (arm_uc_error_t) {ERR_NONE};
00652 }
00653 
00654 arm_uc_error_t ARM_UCSM_AddSource(const ARM_UPDATE_SOURCE *source)
00655 {
00656     if (ARM_UCSM_GetIndexOfSource(source) != MAX_SOURCES) {
00657         // Source already added, don't add again
00658         // TODO should this return ERR_NONE or a new error
00659         // SOMA_ERR_ALREADY_PRESENT?
00660         return (arm_uc_error_t) { ERR_NONE };
00661     }
00662     source->Initialize(ARM_UCSM_CallbackWrapper);
00663     return ARM_UCSM_SourceRegistryAdd(source);
00664 }
00665 
00666 arm_uc_error_t ARM_UCSM_RemoveSource(const ARM_UPDATE_SOURCE *source)
00667 {
00668     arm_uc_error_t err = ARM_UCSM_SourceRegistryRemove(source);
00669     if (err.code == ERR_NONE) {
00670         // Call 'uninitialize' only if the source was found (and removed)
00671         source->Uninitialize();
00672     }
00673     return err;
00674 }
00675 
00676 /* All the `Get` APIs map into `ARM_UCSM_Get` via ARM_UCSM_GetCommon() */
00677 
00678 /**
00679  * @brief invoke Get after setting up params in in-flight store.
00680  */
00681 arm_uc_error_t ARM_UCSM_GetCommon(
00682     arm_uc_uri_t *uri,
00683     arm_uc_buffer_t *buffer,
00684     uint32_t offset,
00685     query_type_t type)
00686 {
00687     ARM_UCSM_RequestStructInit(&request_in_flight);
00688     request_in_flight.uri    = uri;
00689     request_in_flight.buffer = buffer;
00690     request_in_flight.offset = offset;
00691     request_in_flight.type   = type;
00692 
00693     arm_uc_error_t retval = ARM_UCSM_Get(&request_in_flight);
00694     if (retval.code != ERR_NONE) {
00695         ARM_UCSM_SetError(retval);
00696         ARM_UCSM_RequestStructInit(&request_in_flight);
00697     }
00698     return retval;
00699 }
00700 
00701 arm_uc_error_t ARM_UCSM_GetManifest(arm_uc_buffer_t *buffer, uint32_t offset)
00702 {
00703     arm_uc_error_t retval = ARM_UCSM_GetCommon(
00704                                 NULL, buffer, offset, QUERY_TYPE_MANIFEST_DEFAULT);
00705     return retval;
00706 }
00707 
00708 arm_uc_error_t ARM_UCSM_GetManifestFrom(arm_uc_uri_t *uri,
00709                                         arm_uc_buffer_t *buffer,
00710                                         uint32_t offset)
00711 {
00712     arm_uc_error_t retval = ARM_UCSM_GetCommon(
00713                                 uri, buffer, offset, QUERY_TYPE_MANIFEST_URL);
00714     return retval;
00715 }
00716 
00717 arm_uc_error_t ARM_UCSM_GetFirmwareFragment(arm_uc_uri_t *uri,
00718                                             arm_uc_buffer_t *buffer,
00719                                             uint32_t offset)
00720 {
00721     arm_uc_error_t retval = ARM_UCSM_GetCommon(
00722                                 uri, buffer, offset, QUERY_TYPE_FIRMWARE);
00723     return retval;
00724 }
00725 
00726 arm_uc_error_t ARM_UCSM_GetKeytable(arm_uc_uri_t *uri, arm_uc_buffer_t *buffer)
00727 {
00728     arm_uc_error_t retval = ARM_UCSM_GetCommon(
00729                                 uri, buffer, 0, QUERY_TYPE_KEYTABLE);
00730     return retval;
00731 }
00732 
00733 arm_uc_error_t ARM_UCSM_GetError(void)
00734 {
00735     return ucsm_last_error;
00736 }
00737 
00738 arm_uc_error_t ARM_UCSM_SetError(arm_uc_error_t an_error)
00739 {
00740     return (ucsm_last_error = an_error);
00741 }
00742 
00743 
00744 
00745 // INTERFACE.
00746 // ----------
00747 
00748 ARM_UC_SOURCE_MANAGER_t ARM_UC_SourceManager = {
00749     .Initialize          = ARM_UCSM_Initialize,
00750     .Uninitialize        = ARM_UCSM_Uninitialize,
00751     .AddSource           = ARM_UCSM_AddSource,
00752     .RemoveSource        = ARM_UCSM_RemoveSource,
00753     .GetManifest         = ARM_UCSM_GetManifest,
00754     .GetManifestFrom     = ARM_UCSM_GetManifestFrom,
00755     .GetFirmwareFragment = ARM_UCSM_GetFirmwareFragment,
00756     .GetKeytable         = ARM_UCSM_GetKeytable
00757 };