Mayank Gupta / Mbed OS pelion-example-frdm

Dependencies:   FXAS21002 FXOS8700Q

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers arm_uc_resume.c Source File

arm_uc_resume.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-common/arm_uc_config.h"
00020 
00021 #if defined(ARM_UC_FEATURE_RESUME_ENGINE) && (ARM_UC_FEATURE_RESUME_ENGINE == 1)
00022 
00023 #include "update-client-common/arm_uc_common.h"
00024 #include "update-client-resume-engine/arm_uc_resume.h"
00025 
00026 // RESUMPTION MANAGEMENT.
00027 // ----------------------
00028 
00029 // *DEVELOPER-FACING* defines allowing easier testing of parameterised resume.
00030 
00031 // If not available, it becomes extremely difficult to detect exactly when the resume
00032 //   functionality is taking place, or to set values outside of the assumed 'reasonable'
00033 //   range (which can't predict all use cases), which hampers assessment of the settings.
00034 // This can be set in the mbed_app.json file, or here in the source.
00035 // This fills in the values from mbed_app.json if specified non-default.
00036 // Values are checked for sanity unless overridden for easier testing during development.
00037 
00038 #if defined(MBED_CONF_UPDATE_CLIENT_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE)\
00039     && (MBED_CONF_UPDATE_CLIENT_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE)
00040 // Text messages will be printed to output to give live feedback for test.
00041 #define ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE 1
00042 #endif
00043 
00044 // Exponentiation factor tries to balance speed with power considerations.
00045 // Resume is intended to be reasonably aggressive to start but backs off quickly too.
00046 #define RESUME_EXPONENTIATION_FACTOR_LIMIT  1024
00047 #if defined(MBED_CONF_UPDATE_CLIENT_RESUME_EXPONENTIATION_FACTOR)
00048 #define RESUME_EXPONENTIATION_FACTOR        MBED_CONF_UPDATE_CLIENT_RESUME_EXPONENTIATION_FACTOR
00049 #else
00050 #define RESUME_EXPONENTIATION_FACTOR        ARM_UC_RESUME_DEFAULT_EXPONENTIATION_FACTOR
00051 #endif
00052 
00053 // Delay parameters have minimum and maximum values.
00054 // In general the minimum is a hard limit, because going too low will interfere with the algorithm,
00055 //   given that there are various phases which need to coordinate.
00056 // The maximum delays have no hard limits, but issue a warning if they seem unreasonably long,
00057 //   which is intended to catch errors like extra zeroes in the #defined values.
00058 
00059 // Initial delay between resume-servicing attempts.
00060 #if defined(MBED_CONF_UPDATE_CLIENT_RESUME_INITIAL_DELAY_SECS)
00061 #define RESUME_INITIAL_DELAY_MSECS          ((MBED_CONF_UPDATE_CLIENT_RESUME_INITIAL_DELAY_SECS)*1000)
00062 #else
00063 #define RESUME_INITIAL_DELAY_MSECS          ((ARM_UC_RESUME_DEFAULT_INITIAL_DELAY_SECS)*1000)
00064 #endif
00065 
00066 // Greatest delay between resume-servicing attempts.
00067 #if defined(MBED_CONF_UPDATE_CLIENT_RESUME_MAXIMUM_DELAY_SECS)
00068 #define RESUME_MAXIMUM_DELAY_MSECS          ((MBED_CONF_UPDATE_CLIENT_RESUME_MAXIMUM_DELAY_SECS)*1000)
00069 #else
00070 #define RESUME_MAXIMUM_DELAY_MSECS          ((ARM_UC_RESUME_DEFAULT_MAXIMUM_DELAY_SECS)*1000)
00071 #endif
00072 
00073 // Stop resume-servicing attempts after this period has elapsed.
00074 // Max activity time is limited to 30 because of 32-bit limitation of 49 days in msecs.
00075 // Note that the sub-term 'activity' is used here, since resume is intended to be more widely applicable
00076 //   than for any one function alone. For example, downloads would rename this configuration to something
00077 //   like COMM_RESUME_MAXIMUM_DOWNLOAD_TIME_MSECS_LIMIT instead.
00078 
00079 #define RESUME_MAXIMUM_ACTIVITY_TIME_MSECS_LIMIT    (30*24*60*60*1000UL)
00080 
00081 #if defined(MBED_CONF_UPDATE_CLIENT_RESUME_MAXIMUM_ACTIVITY_TIME_SECS)
00082 #define RESUME_MAXIMUM_ACTIVITY_TIME_MSECS  ((MBED_CONF_UPDATE_CLIENT_RESUME_MAXIMUM_ACTIVITY_TIME_SECS)*1000)
00083 #else
00084 #define RESUME_MAXIMUM_ACTIVITY_TIME_MSECS  ((ARM_UC_RESUME_DEFAULT_MAXIMUM_DOWNLOAD_TIME_SECS)*1000)
00085 #endif
00086 
00087 // Interval events, disabled by default.
00088 #define RESUME_INITIAL_INTERVAL_DELAY_MSECS   0
00089 #define RESUME_INITIAL_INTERVAL_COUNT         0
00090 
00091 // Const struct to refill the settings with default startup settings if needed.
00092 arm_uc_resume_t const resume_default_init = {
00093     .exponentiation_factor = RESUME_EXPONENTIATION_FACTOR,
00094     .attempt_initial_delay = RESUME_INITIAL_DELAY_MSECS,
00095     .attempt_max_delay = RESUME_MAXIMUM_DELAY_MSECS,
00096     .activity_max_time = RESUME_MAXIMUM_ACTIVITY_TIME_MSECS,
00097     .interval_delay = RESUME_INITIAL_INTERVAL_DELAY_MSECS,
00098     .interval_count = RESUME_INITIAL_INTERVAL_COUNT,
00099 
00100     .on_resume_interval_p = NULL,
00101     .on_resume_attempt_p = NULL,
00102     .on_resume_terminate_p = NULL,
00103     .on_resume_error_p = NULL
00104 };
00105 
00106 // TRACE.
00107 // ------
00108 
00109 // to disable extra trace, uncomment UC_RESUME_TRACE_ENTRY/VERBOSE/EXIT(...)
00110 // or to enable extra trace, uncomment UC_RESUME_TRACE_ENTRY/VERBOSE/EXIT UC_RESUME_TRACE
00111 #define UC_RESUME_TRACE_ENTRY(...)
00112 #define UC_RESUME_TRACE_VERBOSE(...)
00113 #define UC_RESUME_TRACE_EXIT(...)
00114 //#define UC_RESUME_TRACE_ENTRY UC_RESUME_TRACE
00115 //#define UC_RESUME_TRACE_VERBOSE UC_RESUME_TRACE
00116 //#define UC_RESUME_TRACE_EXIT UC_RESUME_TRACE
00117 
00118 // FORWARD DECLARATIONS.
00119 // ---------------------
00120 
00121 static void check_resume_activity(void const *a_data_p);
00122 static void do_check_resume_activity(uintptr_t unused);
00123 arm_uc_error_t arm_uc_resume_end_monitoring(arm_uc_resume_t *a_resume_p);
00124 
00125 // TIMER MANAGEMENT.
00126 // -----------------
00127 
00128 // Synchronize actual timer state and timer-state variable here.
00129 // Timer_is_running should not be modified at any other point in the code.
00130 
00131 /**
00132  * @brief Keep timer_is_running flag in sync with actual timer state.
00133  * @details Actual access to the timer_is_running field is isolated to one location.
00134  * @param a_resume_p Pointer to the active resume structure.
00135  * @param an_is_running_f Flag to indicate new state of timer.
00136  */
00137 static inline void update_state_resume_timer(arm_uc_resume_t *a_resume_p, bool an_is_running_f)
00138 {
00139     if (a_resume_p != NULL) {
00140         a_resume_p->timer_is_running = an_is_running_f;
00141     }
00142 }
00143 
00144 /**
00145  * @brief Create a new resume timer for use with a resume structure.
00146  * @param a_resume_p Pointer to the active resume structure.
00147  */
00148 static palStatus_t create_resume_timer(arm_uc_resume_t *a_resume_p)
00149 {
00150     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00151     if (a_resume_p == NULL) {
00152         return PAL_ERR_INVALID_ARGUMENT ;
00153     }
00154     update_state_resume_timer(a_resume_p, false);
00155     palStatus_t status = pal_osTimerCreate(check_resume_activity, a_resume_p, palOsTimerOnce , &a_resume_p->timer_id);
00156     if (status == PAL_SUCCESS) {
00157         UC_RESUME_TRACE_VERBOSE("success: resume-timer %" PRIx32 " created", (uint32_t)a_resume_p->timer_id);
00158     } else {
00159         UC_RESUME_ERR_MSG("error: resume-timer %" PRIx32 " could not create new timer %" PRIx32,
00160                           (uint32_t)a_resume_p->timer_id, (uint32_t)status);
00161     }
00162     return status;
00163 }
00164 
00165 /**
00166  * @brief Stop the timer in a resume structure.
00167  * @param a_resume_p Pointer to the active resume structure.
00168  */
00169 static palStatus_t stop_resume_timer(arm_uc_resume_t *a_resume_p)
00170 {
00171     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00172     if (a_resume_p == NULL) {
00173         return PAL_ERR_INVALID_ARGUMENT ;
00174     }
00175     palStatus_t status = PAL_SUCCESS;
00176     // this should not be called if the timer is already stopped.
00177     if (!a_resume_p->timer_is_running) {
00178         UC_RESUME_TRACE_VERBOSE("warning: resume-timer is already stopped.");
00179     } else {
00180         status = pal_osTimerStop((palTimerID_t)(a_resume_p->timer_id));
00181         update_state_resume_timer(a_resume_p, false);
00182         if (status != PAL_SUCCESS) {
00183             UC_RESUME_ERR_MSG("error: resume-timer %" PRIx32 " could not stop %" PRIx32,
00184                               (uint32_t)a_resume_p->timer_id, (uint32_t)status);
00185         }
00186     }
00187     return status;
00188 }
00189 
00190 /**
00191  * @brief Start the timer in a resume structure.
00192  * @param a_resume_p Pointer to the active resume structure.
00193  */
00194 static palStatus_t start_resume_timer(arm_uc_resume_t *a_resume_p)
00195 {
00196     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00197     if (a_resume_p == NULL) {
00198         return PAL_ERR_INVALID_ARGUMENT ;
00199     }
00200     palStatus_t status = PAL_SUCCESS;
00201     // this should not be called if the timer is already running.
00202     // but if so, needs to be stopped then restarted with new delay.
00203     if (a_resume_p->timer_is_running) {
00204         UC_RESUME_TRACE_VERBOSE("warning: resume-timer is already running.");
00205         stop_resume_timer(a_resume_p);
00206     }
00207     // now start the timer with the new delay value.
00208     status = pal_osTimerStart((palTimerID_t)(a_resume_p->timer_id), a_resume_p->actual_delay);
00209     update_state_resume_timer(a_resume_p, status == PAL_SUCCESS);
00210     if (status != PAL_SUCCESS) {
00211         UC_RESUME_ERR_MSG("error: resume-timer %" PRIx32 " could not start %" PRIx32,
00212                           (uint32_t)a_resume_p->timer_id, (uint32_t)status);
00213     }
00214     return status;
00215 }
00216 
00217 // UTILITY FUNCTIONS.
00218 // ------------------
00219 
00220 /**
00221  * @brief Make a call to the requested handler.
00222  * @param a_resume_p Pointer to the active resume structure.
00223  */
00224 static void invoke_resume_handler(arm_uc_resume_t *a_resume_p, on_resume_cb_t a_handler_p, char *a_text_p)
00225 {
00226     if (a_resume_p == NULL) {
00227         UC_RESUME_ERR_MSG("error: %s resume-pointer is null!", a_text_p);
00228     } else if (a_handler_p != NULL) {
00229         UC_RESUME_TRACE_VERBOSE("calling on-%s handler", a_text_p);
00230         a_handler_p(a_resume_p->context_p);
00231     } else {
00232         UC_RESUME_ERR_MSG("error: %s handler-vector is null!", a_text_p);
00233     }
00234 }
00235 /**
00236  * @brief Reset values per every new resume-attempt.
00237  * @param a_resume_p Pointer to the active resume structure.
00238  */
00239 static void reset_on_attempt_values(arm_uc_resume_t *a_resume_p)
00240 {
00241     if (a_resume_p != NULL) {
00242         a_resume_p->num_intervals = 0;
00243         a_resume_p->sum_attempt_period = 0;
00244         a_resume_p->sum_interval_delays = 0;
00245     }
00246 }
00247 /**
00248  * @brief Reset values per every new resume-cycle.
00249  * @param a_resume_p Pointer to the active resume structure.
00250  */
00251 static void reset_on_cycle_values(arm_uc_resume_t *a_resume_p)
00252 {
00253     if (a_resume_p != NULL) {
00254         a_resume_p->num_attempts = 0;
00255     }
00256 }
00257 /**
00258  * @brief Reset values per every new resume-start.
00259  * @param a_resume_p Pointer to the active resume structure.
00260  */
00261 static void reset_on_start_values(arm_uc_resume_t *a_resume_p)
00262 {
00263     if (a_resume_p != NULL) {
00264         a_resume_p->num_attempts = 0;
00265         a_resume_p->sum_total_period = 0;
00266     }
00267 }
00268 
00269 // DELAY CALCULATIONS.
00270 // -------------------
00271 
00272 // Utility functions to simplify caller code.
00273 static void set_below(uint32_t *a_value_p, uint32_t a_high)
00274 {
00275     if (*a_value_p > a_high) {
00276         *a_value_p = a_high;
00277     }
00278 }
00279 static void set_between(uint32_t *a_value_p, uint32_t a_low, uint32_t a_high)
00280 {
00281     if (*a_value_p > a_high) {
00282         *a_value_p = a_high;
00283     } else if (*a_value_p < a_low) {
00284         *a_value_p = a_low;
00285     }
00286 }
00287 
00288 // @brief Calculate an interval randomised around the given value.
00289 // @details Get a 25% (+-12.5%) randomisation of the base interval value,
00290 //          calculated by multiplying it by a 0-maxuint random factor,
00291 //          and add/subtract it to the base by adding with offset.
00292 //          Picking these values because they are reasonable but quick to calculate.
00293 // @param an_interval The base interval around which to randomise.
00294 // @return Interval +/- some random part of one-eighth of the interval.
00295 #define RANGE_FRACTION_DIV 4
00296 #define BOUND_FRACTION_DIV 8
00297 static uint32_t randomised_interval(uint32_t an_interval)
00298 {
00299     // Calculate range for max amount of wiggle allowed in both directions total.
00300     uint32_t range = an_interval / RANGE_FRACTION_DIV;
00301     // Calculate bound for max allowed wiggle *either* above or below the interval.
00302     uint32_t bound = an_interval / BOUND_FRACTION_DIV;
00303     // Actual wiggle for this attempt.
00304     // Calculated by multiplying max allowed wiggle by a fraction of rand()/RAND_MAX.
00305     // Use (RAND_MAX+1) if is power-of-two and not zero
00306 #if (((RAND_MAX+1) & RAND_MAX) == 0) && ((RAND_MAX+1) != 0)
00307     uint32_t wiggle = ((long long) rand() * range) / ((unsigned) RAND_MAX + 1);
00308 #else
00309     uint32_t wiggle = ((long long)rand() * range) / ((unsigned)RAND_MAX);
00310 #endif
00311     return an_interval - bound + wiggle;
00312 }
00313 
00314 /**
00315  * @brief Calculate values for next attempt of a resume cycle.
00316  * @details This calculates the time until this current resume attempt is scheduled to
00317  *            time out, ignoring the possibility of interval events before then.
00318  * @param a_resume_p Pointer to the active resume structure.
00319  * @param an_expected_delay The expected delay prior to jitter and limit adjustment.
00320  */
00321 static void calc_next_attempt_jittered_delay(arm_uc_resume_t *a_resume_p, uint32_t an_expected_delay)
00322 {
00323     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ", %" PRIu32 ")", __func__, (uint32_t)a_resume_p, (uint32_t)an_expected_delay);
00324     if (a_resume_p == NULL) {
00325         UC_RESUME_ERR_MSG(".. %s exiting with NULL resume-pointer", __func__);
00326         return;
00327     }
00328     // Clear admin values keeping track of progress through this attempt.
00329     reset_on_attempt_values(a_resume_p);
00330 
00331     // Get the correct interval times set up.
00332     // Ensure they are within range of maximum and minimum allowed.
00333     a_resume_p->expected_delay = an_expected_delay;
00334     set_between(&a_resume_p->expected_delay, a_resume_p->attempt_initial_delay, a_resume_p->attempt_max_delay);
00335     // Set jittered delay, which is randomised, bounded by max and min times.
00336     a_resume_p->jitter_delay = randomised_interval(a_resume_p->expected_delay);
00337     set_below(&a_resume_p->jitter_delay, a_resume_p->activity_max_time - a_resume_p->sum_total_period);
00338     set_between(&a_resume_p->jitter_delay, a_resume_p->attempt_initial_delay, a_resume_p->attempt_max_delay);
00339     a_resume_p->saved_jitter_delay = a_resume_p->jitter_delay;
00340 
00341     UC_RESUME_TRACE_EXIT(".. %s: jitter delay %" PRIu32 " msecs", __func__, a_resume_p->jitter_delay);
00342 }
00343 
00344 /**
00345  * @brief Calculate values for first attempt of a new resume cycle.
00346  * @details This calculates the time until this current resume attempt is scheduled to
00347  *            time out, ignoring the possibility of interval events before then.
00348  * @param a_resume_p Pointer to the active resume structure.
00349  */
00350 static void calc_initial_attempt_jittered_delay(arm_uc_resume_t *a_resume_p)
00351 {
00352     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00353 
00354     reset_on_cycle_values(a_resume_p);
00355     calc_next_attempt_jittered_delay(a_resume_p, a_resume_p->attempt_initial_delay);
00356 }
00357 
00358 /**
00359  * @brief Calculate *actual* values for jittered delay taking intervals into account.
00360  * @details This is the value to which the timer will be set, and will possibly be different
00361  *            than the jittered delay, which is the time to the next resume attempt, and
00362  *            does not take into account the need for any interval events before that.
00363  *            For this reason, the actual timeout calculation will first track all necessary
00364  *            interval events, and after these have been completed, or would overrun the
00365  *            jittered resume-attempt timeout, it returns the jittered timeout.
00366  * @param a_resume_p Pointer to the active resume structure.
00367  */
00368 static void calc_next_actual_delay(arm_uc_resume_t *a_resume_p)
00369 {
00370     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00371     if (a_resume_p == NULL) {
00372         return;
00373     }
00374     // Calculate the period to the next timer event.
00375     uint32_t interval = a_resume_p->interval_delay;
00376     if ((interval != 0)
00377             && (a_resume_p->num_intervals < a_resume_p->interval_count)
00378             && ((a_resume_p->sum_interval_delays + interval) < a_resume_p->jitter_delay)) {
00379         a_resume_p->sum_interval_delays += interval;
00380         a_resume_p->actual_delay = interval;
00381     } else {
00382         a_resume_p->actual_delay = a_resume_p->jitter_delay - a_resume_p->sum_interval_delays;
00383     }
00384     UC_RESUME_TRACE_EXIT(".. %s: actual delay %" PRIu32 " msecs", __func__, a_resume_p->actual_delay);
00385 }
00386 
00387 // CHECKING OF RESUME EVENTS.
00388 // --------------------------
00389 
00390 /**
00391  * @brief Post callback to avoid running from timer callback context
00392  * @param a_data_p Pointer to the active resume structure.
00393  */
00394 static void check_resume_activity(void const *a_data_p)
00395 {
00396     arm_uc_resume_t *resume_p = (arm_uc_resume_t *) a_data_p;
00397     if (resume_p == NULL) {
00398         UC_RESUME_ERR_MSG("resume-struct pointer is null in check_resume_activity!");
00399     } else {
00400         // Keep the timer and timer-state variable synched - it stops on firing (once-off).
00401         update_state_resume_timer(resume_p, false);
00402         // Only invoke the checks if the resume engine hasn't been disabled.
00403         if (resume_p->currently_resuming) {
00404             // If it doesn't install, run an error-specific callback.
00405             // Can't do something specific-to-resume, because user needs are different.
00406             ARM_UC_PostCallback(NULL, do_check_resume_activity, (uintptr_t)resume_p);
00407         }
00408     }
00409 }
00410 
00411 /**
00412  * @brief Evaluate resume probe - kill, install a new idle probe, or initiate a new operation.
00413  * @param a_param Parameter to be passed to callback, is pointer to resume struct.
00414  */
00415 static void do_check_resume_activity(uintptr_t a_param)
00416 {
00417     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, a_param);
00418 
00419     arm_uc_resume_t *a_resume_p = (arm_uc_resume_t *)a_param;
00420     if (a_resume_p == NULL) {
00421         UC_RESUME_ERR_MSG("%s resume-struct pointer is null!", __func__);
00422     } else {
00423         // Update summed periods, total and per attempt.
00424         a_resume_p->sum_total_period += a_resume_p->actual_delay;
00425         a_resume_p->sum_attempt_period += a_resume_p->actual_delay;
00426 
00427         // Identify the current state of affairs for the resume probe.
00428         if (!a_resume_p->currently_resuming) {
00429             // Must have been terminated *after* the timer had posted the callback.
00430             // Just ignore this and let the thread die naturally.
00431 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00432             UC_QA_TRACE("\nResume inactive.\n");
00433 #endif
00434         } else if (a_resume_p->sum_total_period >= a_resume_p->activity_max_time) {
00435             UC_RESUME_TRACE("resume max-activity-time reached - %" PRIu32 " secs",
00436                             a_resume_p->activity_max_time / 1000);
00437             // Past the maximum time we should keep trying, so just let it die.
00438             a_resume_p->currently_resuming = false;
00439             // Make the callback and let handler take care of it.
00440             invoke_resume_handler(a_resume_p, a_resume_p->on_resume_terminate_p, "resume-terminate");
00441 
00442 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00443             puts("\nMaximum resume activity time reached.\n");
00444 #endif
00445         } else if (a_resume_p->sum_attempt_period >= a_resume_p->jitter_delay) {
00446             UC_RESUME_TRACE("resume-attempt period reached - %" PRIu32 " secs",
00447                             a_resume_p->expected_delay / 1000);
00448             UC_RESUME_TRACE_VERBOSE("resume attempted after total %" PRIu32 " secs",
00449                                     a_resume_p->sum_total_period / 1000);
00450 
00451             // Let the source know this will be a resume attempt (there is some setup involved),
00452             //   put the state machine in a suitable state, and then initiate the process.
00453             // Need to reset the various timers and carry on.
00454             ++a_resume_p->num_attempts;
00455             calc_next_attempt_jittered_delay(a_resume_p,
00456                                              a_resume_p->expected_delay * a_resume_p->exponentiation_factor);
00457             calc_next_actual_delay(a_resume_p);
00458 
00459             invoke_resume_handler(a_resume_p, a_resume_p->on_resume_attempt_p, "resume-attempt");
00460             start_resume_timer(a_resume_p);
00461 
00462 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00463             // Resume is running, is not deferred, internal limit has not been reached.
00464             //   and the hub is currently in the idle state, so we need to try again.
00465             // But first we need to enqueue the next resume check.
00466             // If it isn't deferred or cancelled in the meantime,
00467             //   it will take action when it fires.
00468             UC_QA_TRACE("\nResume being attempted now, next in %" PRIi32 ".%" PRIu32 "(~%" PRIi32 ")\n",
00469                         a_resume_p->jitter_delay / 1000,
00470                         (a_resume_p->jitter_delay % 1000) / 100,
00471                         a_resume_p->expected_delay / 1000);
00472 #endif
00473         } else {
00474             // This must be an interval event.
00475             UC_RESUME_TRACE_VERBOSE("resume interval period reached - %" PRIu32 " msecs (at %" PRIu32 " of %" PRIu32 ")",
00476                                     a_resume_p->interval_delay, a_resume_p->sum_attempt_period, a_resume_p->jitter_delay);
00477             ++a_resume_p->num_intervals;
00478             calc_next_actual_delay(a_resume_p);
00479 
00480             if (a_resume_p->num_intervals <= a_resume_p->interval_count) {
00481                 invoke_resume_handler(a_resume_p, a_resume_p->on_resume_interval_p, "resume-interval");
00482             }
00483             start_resume_timer(a_resume_p);
00484         }
00485     }
00486     UC_RESUME_TRACE_EXIT(".. %s", __func__);
00487 }
00488 
00489 // INIT, START, RESYNCH, END.
00490 // --------------------------
00491 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00492 static bool has_displayed_resume_settings = false;
00493 #endif
00494 
00495 /**
00496  * @brief Check a client resume-struct with values to be used for resuming.
00497  * @param a_resume_p A pointer to the struct holding the values to be checked.
00498  */
00499 arm_uc_error_t arm_uc_resume_check_settings(arm_uc_resume_t *a_resume_p)
00500 {
00501     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00502     ARM_UC_INIT_ERROR(result, ERR_NONE);
00503 
00504     if (a_resume_p == NULL) {
00505         ARM_UC_SET_ERROR(result, ERR_NULL_PTR);
00506         UC_RESUME_ERR_MSG("%s failed: null for resume-struct", __func__);
00507     }
00508     if (ARM_UC_IS_NOT_ERROR(result)) {
00509         // Check that the new values satisfy the constraints.
00510         // There are no constraints on intervals, they just won't be called if out of range.
00511         if ((a_resume_p->exponentiation_factor == 0)
00512                 || (a_resume_p->exponentiation_factor > RESUME_EXPONENTIATION_FACTOR_LIMIT)
00513                 || (a_resume_p->attempt_initial_delay == 0)
00514                 || (a_resume_p->attempt_initial_delay > a_resume_p->attempt_max_delay)
00515                 || (a_resume_p->attempt_max_delay == 0)
00516                 || (a_resume_p->attempt_max_delay > a_resume_p->activity_max_time)
00517                 || (a_resume_p->activity_max_time == 0)
00518                 || (a_resume_p->activity_max_time > RESUME_MAXIMUM_ACTIVITY_TIME_MSECS_LIMIT)) {
00519             ARM_UC_SET_ERROR(result, ERR_INVALID_PARAMETER);
00520             UC_RESUME_ERR_MSG("%s failed: invalid resume settings", __func__);
00521         }
00522     }
00523 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00524     if (!has_displayed_resume_settings) {
00525         if (ARM_UC_IS_ERROR(result)) {
00526             UC_QA_ERR_MSG("Resume settings invalid - checking failed.\r\n");
00527         }
00528     }
00529 #endif
00530     UC_RESUME_TRACE_EXIT(".. %s %" PRIx32, __func__, (uint32_t)result.code);
00531     return result;
00532 }
00533 
00534 /**
00535  * @brief Initialize a client resume-struct with values to be used for resuming.
00536  * @param a_resume_p A pointer to the struct to be initialized with values.
00537  * @param an_exponentiation_factor The factor by which a previous delay is multiplied to get next.
00538  * @param an_attempt_initial_delay The smallest allowed gap between successive attempts.
00539  * @param an_attempt_max_delay The largest allowed gap between successive attempts.
00540  * @param an_activity_max_time The largest *total* allowed period for full cycle of activity.
00541  * @param an_interval_delay The gap between regular interval events until next.
00542  * @param an_interval_count The number of interval events to allow per resume attempt.
00543  * @param an_on_interval_cb The callback to be invoked on an interval event.
00544  * @param an_on_attempt_cb The callback to be invoked on an attempt event.
00545  * @param an_on_termination_cb The callback to be invoked on a termination event.
00546  * @param an_on_error_cb The callback to be invoked on an error event (runs in ISR context!).
00547  */
00548 arm_uc_error_t arm_uc_resume_initialize(
00549     arm_uc_resume_t *a_resume_p,
00550     uint32_t an_exponentiation_factor,
00551     uint32_t an_attempt_initial_delay,
00552     uint32_t an_attempt_max_delay,
00553     uint32_t an_activity_max_time,
00554     uint32_t an_interval_delay,
00555     uint32_t an_interval_count,
00556     on_resume_cb_t an_on_interval_cb,
00557     on_resume_cb_t an_on_attempt_cb,
00558     on_resume_cb_t an_on_termination_cb,
00559     on_resume_cb_t an_on_error_cb,
00560     void *a_context_p)
00561 {
00562     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00563     ARM_UC_INIT_ERROR(result, ERR_NONE);
00564 
00565     if (a_resume_p == NULL) {
00566         ARM_UC_SET_ERROR(result, ERR_NULL_PTR);
00567         UC_RESUME_ERR_MSG("%s failed: null for resume-struct", __func__);
00568     }
00569     if (ARM_UC_IS_NOT_ERROR(result)) {
00570         a_resume_p->exponentiation_factor = an_exponentiation_factor;
00571         a_resume_p->attempt_initial_delay = an_attempt_initial_delay;
00572         a_resume_p->attempt_max_delay = an_attempt_max_delay;
00573         a_resume_p->activity_max_time = an_activity_max_time;
00574         a_resume_p->interval_delay = an_interval_delay;
00575         a_resume_p->interval_count = an_interval_count;
00576         a_resume_p->on_resume_interval_p = an_on_interval_cb;
00577         a_resume_p->on_resume_attempt_p = an_on_attempt_cb;
00578         a_resume_p->on_resume_terminate_p = an_on_termination_cb;
00579         a_resume_p->on_resume_error_p = an_on_error_cb;
00580         a_resume_p->context_p = a_context_p;
00581 
00582         result = arm_uc_resume_check_settings(a_resume_p);
00583     }
00584 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00585     if (!has_displayed_resume_settings) {
00586         UC_QA_TRACE("Resume settings\r\n"
00587                     "exponentiation factor: %" PRIu32
00588                     ", initial delay: %" PRIu32
00589                     ", maximum delay: %" PRIu32
00590                     ", maximum activity time: %" PRIu32
00591                     ", interval delay: %" PRIu32
00592                     ", interval count: %" PRIu32 "\r\n",
00593                     an_exponentiation_factor,
00594                     an_attempt_initial_delay, an_attempt_max_delay, an_activity_max_time,
00595                     an_interval_delay, an_interval_count);
00596         if (ARM_UC_IS_ERROR(result)) {
00597             UC_QA_ERR_MSG("Resume settings invalid - copying failed.\r\n");
00598         }
00599         has_displayed_resume_settings = true;
00600     }
00601 #endif
00602     UC_RESUME_TRACE_EXIT(".. %s %" PRIx32, __func__, (uint32_t)result.code);
00603     return result;
00604 }
00605 
00606 /**
00607  * @brief Start off a new resume monitor, including initialisation.
00608  * @details Initialise a supplied resume-struct with suitable values as passed in,
00609  *            then initiate the process of monitoring for resume purposes.
00610  * @param a_resume_p Pointer to the active resume structure.
00611  * @param a_resume_init_p Pointer to the initial values for active resume structure.
00612  */
00613 arm_uc_error_t arm_uc_resume_start_monitoring(
00614     arm_uc_resume_t *a_resume_p)
00615 {
00616     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00617     ARM_UC_INIT_ERROR(result, ERR_NONE);
00618 
00619     if (a_resume_p == NULL) {
00620         ARM_UC_SET_ERROR(result, ERR_NULL_PTR);
00621         UC_RESUME_ERR_MSG("%s failed: null for resume-struct", __func__);
00622     }
00623     if (ARM_UC_IS_NOT_ERROR(result)) {
00624         // A new resume must first abort an ongoing resume-nudge attempt.
00625         if (a_resume_p->currently_resuming) {
00626             result = arm_uc_resume_end_monitoring(a_resume_p);
00627             if (ARM_UC_IS_ERROR(result)) {
00628                 UC_RESUME_ERR_MSG("%s failed: could not end existing resume", __func__);
00629             }
00630         }
00631     }
00632     // Set up the starting conditions for a full new resume cycle.
00633     // Note that this assumes a brand new cycle, all the counters & timers are reset.
00634     if (ARM_UC_IS_NOT_ERROR(result)) {
00635         result = arm_uc_resume_check_settings(a_resume_p);
00636     }
00637     if (ARM_UC_IS_NOT_ERROR(result)) {
00638         // If the timer hasn't even been created yet, do it now.
00639         if (a_resume_p->timer_id == 0) {
00640             palStatus_t pal_status = create_resume_timer(a_resume_p);
00641             if (pal_status != PAL_SUCCESS) {
00642                 ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
00643             }
00644         }
00645     }
00646     if (ARM_UC_IS_NOT_ERROR(result)) {
00647         a_resume_p->currently_resuming = true;
00648 
00649         reset_on_start_values(a_resume_p);
00650         calc_initial_attempt_jittered_delay(a_resume_p);
00651         calc_next_actual_delay(a_resume_p);
00652 
00653         // Enable a timed callback for the next check, wait for it.
00654         palStatus_t pal_status = start_resume_timer(a_resume_p);
00655         if (pal_status != PAL_SUCCESS) {
00656             ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
00657             UC_RESUME_ERR_MSG("%s failed: could not start timer, error %"
00658                               PRIx32 " id %" PRIu32 " delay %" PRIu32,
00659                               __func__, (uint32_t)pal_status, (uint32_t)a_resume_p->timer_id, a_resume_p->actual_delay);
00660         }
00661 #if ARM_UC_RESUME_ATTEMPT_TEST_MESSAGES_ENABLE
00662         UC_QA_TRACE("\nResume starting now, in %" PRIi32 ".%" PRIu32 "(~%" PRIi32 ")\n",
00663                     a_resume_p->jitter_delay / 1000,
00664                     (a_resume_p->jitter_delay % 1000) / 100,
00665                     a_resume_p->expected_delay / 1000);
00666 #endif
00667     }
00668     UC_RESUME_TRACE_EXIT(".. %s %" PRIx32, __func__, (uint32_t)result.code);
00669     return result;
00670 }
00671 
00672 /**
00673  * @brief Notify resume probe that recent valid activity has taken place.
00674  * @details Reset the resume engine such that timing begins again from now.
00675  * @param a_resume_p Pointer to the active resume structure.
00676  */
00677 
00678 arm_uc_error_t arm_uc_resume_resynch_monitoring(arm_uc_resume_t *a_resume_p)
00679 {
00680     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00681     ARM_UC_INIT_ERROR(result, ERR_NONE);
00682 
00683     if (a_resume_p == NULL) {
00684         UC_RESUME_ERR_MSG("resume-resynch failed: null-pointer");
00685         ARM_UC_SET_ERROR(result, ERR_NULL_PTR);
00686     } else if (a_resume_p->timer_id == 0) {
00687         UC_RESUME_ERR_MSG("resume-resynch failed: timer-id == 0");
00688         ARM_UC_SET_ERROR(result, ERR_INVALID_PARAMETER);
00689     }
00690     if (ARM_UC_IS_NOT_ERROR(result)) {
00691         palStatus_t pal_status = stop_resume_timer(a_resume_p);
00692         if (pal_status != PAL_SUCCESS) {
00693             UC_RESUME_ERR_MSG("resume-resynch failed: could not stop timer, error %" PRIx32, (uint32_t)pal_status);
00694         }
00695     }
00696     if (ARM_UC_IS_NOT_ERROR(result)) {
00697         // Clear attempts control values and restore the prior jitter delay.
00698         // Jittered delay is kept the same as it was before the reset.
00699         reset_on_attempt_values(a_resume_p);
00700         reset_on_cycle_values(a_resume_p);
00701         a_resume_p->jitter_delay = a_resume_p->saved_jitter_delay;
00702         calc_next_actual_delay(a_resume_p);
00703 
00704         palStatus_t pal_status = start_resume_timer(a_resume_p);
00705         if (pal_status != PAL_SUCCESS) {
00706             ARM_UC_SET_ERROR(result, ERR_UNSPECIFIED);
00707             UC_RESUME_ERR_MSG("resume-resynch failed: could not start timer, error %"
00708                               PRIx32 " id %" PRIx32 " delay %" PRIu32,
00709                               (uint32_t)pal_status, (uint32_t)a_resume_p->timer_id, a_resume_p->actual_delay);
00710         }
00711         else {
00712             UC_RESUME_TRACE_VERBOSE("resume-resynch successful: Next check in %" PRIu32 ".%3" PRIu32 " secs",
00713                                         a_resume_p->actual_delay / 1000, a_resume_p->actual_delay % 1000);
00714         }
00715     }
00716     UC_RESUME_TRACE_EXIT(".. %s %" PRIx32, __func__, (uint32_t)result.code);
00717     return result;
00718 }
00719 
00720 /**
00721  * @brief Notify resume probe that full firmware download has completed.
00722  * @details Halt the resume engine running on this resume-struct.
00723  * @param a_resume_p Pointer to the active resume structure.
00724  */
00725 arm_uc_error_t arm_uc_resume_end_monitoring(arm_uc_resume_t *a_resume_p)
00726 {
00727     UC_RESUME_TRACE_ENTRY(">> %s (%" PRIx32 ")", __func__, (uint32_t)a_resume_p);
00728     ARM_UC_INIT_ERROR(result, ERR_NONE);
00729 
00730     if (a_resume_p == NULL) {
00731         ARM_UC_SET_ERROR(result, ERR_NULL_PTR);
00732     }
00733     if (ARM_UC_IS_NOT_ERROR(result)) {
00734         a_resume_p->currently_resuming = false;
00735         if (a_resume_p->timer_id != 0) {
00736             if (stop_resume_timer(a_resume_p) != PAL_SUCCESS) {
00737                 UC_RESUME_ERR_MSG("resume-end failed: could not stop timer");
00738             }
00739         }
00740     }
00741     UC_RESUME_TRACE_EXIT(".. %s %" PRIx32, __func__, (uint32_t)result.code);
00742     return result;
00743 }
00744 
00745 #endif // ARM_UC_FEATURE_RESUME_ENGINE