Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: FXAS21002 FXOS8700Q
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
Generated on Tue Jul 12 2022 20:20:57 by
