mirror of
https://github.com/google/pebble.git
synced 2025-05-29 14:33:12 +00:00
173 lines
6.5 KiB
C
173 lines
6.5 KiB
C
/*
|
|
* Copyright 2024 Google LLC
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "kernel_applib_state.h"
|
|
|
|
#include "applib/ui/layer.h"
|
|
#include "mcu/interrupts.h"
|
|
#include "os/mutex.h"
|
|
|
|
#include "FreeRTOS.h"
|
|
#include "task.h"
|
|
|
|
static PebbleRecursiveMutex *s_log_state_mutex = INVALID_MUTEX_HANDLE;
|
|
static bool s_log_state_task_entered[NumPebbleTask]; // which tasks have entered
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
CompassServiceConfig **kernel_applib_get_compass_config(void) {
|
|
static CompassServiceConfig *s_compass_config;
|
|
return &s_compass_config;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
AnimationState* kernel_applib_get_animation_state(void) {
|
|
static AnimationState s_kernel_animation_state;
|
|
return &s_kernel_animation_state;
|
|
}
|
|
|
|
// Get the current task. If FreeRTOS has not been initialized yet, set to KernelMain
|
|
static PebbleTask prv_get_current_task(void) {
|
|
if (pebble_task_get_handle_for_task(PebbleTask_KernelMain) == NULL) {
|
|
return PebbleTask_KernelMain;
|
|
} else {
|
|
return pebble_task_get_current();
|
|
}
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
// Return a pointer to the LogState to use for kernel (non app task) code. The LogState contains
|
|
// the buffers for formatting the log message.
|
|
// Returns NULL if a kernel logging operation is already in progress
|
|
LogState *kernel_applib_get_log_state(void) {
|
|
static LogState sys_log_state;
|
|
bool use_mutex;
|
|
|
|
// Return right away if we re-entered from the same task For example, if we hit an assert while
|
|
// trying to grab the s_log_state_mutex mutex below and tried to log an error.
|
|
PebbleTask task = prv_get_current_task();
|
|
if (s_log_state_task_entered[task]) {
|
|
return NULL;
|
|
}
|
|
s_log_state_task_entered[task] = true;
|
|
|
|
|
|
// We have 3 possible phases of operation:
|
|
// 1.) Before FreeRTOS has been initialized - only 1 "task", no mutexes available
|
|
// 2.) After FreeRTOS, but before our mutex has been created (via kernel_applib_init())
|
|
// 3.) After our mutex has been created.
|
|
// In phase 1, we don't bother taking the mutex but still log
|
|
// In phase 2, we just return without logging. It is too dangerous to have
|
|
// possibly multiple tasks using logging without mutex support
|
|
// In phase 3, we log after locking the mutex only.
|
|
// Note, if we are in an ISR or critical section in any of these phases, we cannot use a mutex
|
|
if ((pebble_task_get_handle_for_task(PebbleTask_KernelMain) == NULL) || mcu_state_is_isr()
|
|
|| portIN_CRITICAL() || (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING)) {
|
|
// phase 1 || in an ISR || in a critical section
|
|
use_mutex = false;
|
|
} else if (s_log_state_mutex == INVALID_MUTEX_HANDLE) {
|
|
// phase 2
|
|
dbgserial_putstr("LOGGING DISABLED");
|
|
goto exit_fail;
|
|
} else {
|
|
// phase 3
|
|
use_mutex = true;
|
|
}
|
|
|
|
if (use_mutex) {
|
|
// Logging operations shouldn't take long to complete. Use a timeout in case we run into
|
|
// an unlikely deadlock situation (one task doing a synchronous log to flash and another task
|
|
// trying to log from flash code)
|
|
bool success = mutex_lock_recursive_with_timeout(s_log_state_mutex, 1000);
|
|
if (!success) {
|
|
dbgserial_putstr("kernel_applib_get_log_state timeout error");
|
|
goto exit_fail;
|
|
}
|
|
}
|
|
|
|
// Return if re-entered (logging while logging). This can happen for example if one task
|
|
// grabbed the context from an ISR or critical section and another grabbed it using the mutex
|
|
if (sys_log_state.in_progress) {
|
|
if (use_mutex) {
|
|
mutex_unlock_recursive(s_log_state_mutex);
|
|
}
|
|
goto exit_fail;
|
|
}
|
|
|
|
sys_log_state.in_progress = true;
|
|
return &sys_log_state;
|
|
|
|
exit_fail:
|
|
s_log_state_task_entered[task] = false;
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
// Release the LogState buffer obtained by kernel_applib_get_log_state()
|
|
void kernel_applib_release_log_state(LogState *state) {
|
|
state->in_progress = false;
|
|
|
|
// For phase 1 & when in an ISR, there is no mutex available
|
|
if (!portIN_CRITICAL() && !mcu_state_is_isr() &&
|
|
(xTaskGetSchedulerState() == taskSCHEDULER_RUNNING) &&
|
|
(s_log_state_mutex != INVALID_MUTEX_HANDLE)) {
|
|
mutex_unlock_recursive(s_log_state_mutex);
|
|
}
|
|
|
|
// Clear the reentrancy flag for this task
|
|
PebbleTask task = prv_get_current_task();
|
|
s_log_state_task_entered[task] = false;
|
|
}
|
|
|
|
|
|
// ---------------------------------------------------------------------------------------------
|
|
EventServiceInfo* kernel_applib_get_event_service_state(void) {
|
|
static EventServiceInfo s_event_service_state;
|
|
return &s_event_service_state;
|
|
}
|
|
|
|
// --------------------------------------------------------------------------------------------
|
|
TickTimerServiceState* kernel_applib_get_tick_timer_service_state(void) {
|
|
static TickTimerServiceState s_tick_timer_service_state;
|
|
return &s_tick_timer_service_state;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
ConnectionServiceState* kernel_applib_get_connection_service_state(void) {
|
|
static ConnectionServiceState s_connection_service_state;
|
|
return &s_connection_service_state;
|
|
}
|
|
|
|
// -----------------------------------------------------------------------------------------------------------
|
|
BatteryStateServiceState* kernel_applib_get_battery_state_service_state(void) {
|
|
static BatteryStateServiceState s_battery_state_service_state;
|
|
return &s_battery_state_service_state;
|
|
}
|
|
|
|
Layer** kernel_applib_get_layer_tree_stack(void) {
|
|
static Layer* layer_tree_stack[LAYER_TREE_STACK_SIZE];
|
|
return layer_tree_stack;
|
|
}
|
|
|
|
// -------------------------------------------------------------------------------------------------------------
|
|
void kernel_applib_init(void) {
|
|
s_log_state_mutex = mutex_create_recursive();
|
|
connection_service_state_init(kernel_applib_get_connection_service_state());
|
|
battery_state_service_state_init(kernel_applib_get_battery_state_service_state());
|
|
}
|
|
|
|
|