State Management
Overview
The Ma Bell Gateway uses a centralized state management system that provides efficient state tracking and task synchronization. The system is designed around bitmasks for state representation and FreeRTOS task notifications for state change awareness.
Key Features
Bitmask-based State Representation - Each state category (phone, bluetooth, network, system) uses an 8-bit mask - Each bit represents a specific state condition - Multiple states can be active simultaneously
State Change Notifications - Tasks can register to receive notifications when specific states change - Uses FreeRTOS task notifications for efficient task synchronization - No polling required - tasks sleep until state changes
State Categories
Phone State (8 bits) - Off hook status - Ringing status - Dialing status - Various tone states
Bluetooth State (8 bits) - Connection status - Call status - Audio status - Mute states
Network State (8 bits) - WiFi connection - IP acquisition - DNS status - Web server status
System State (8 bits) - Initialization status - Error conditions - Battery status - System modes
Thread Safety
The state engine is designed for use in a multi-threaded environment but requires careful consideration of thread safety. Here are the key points and patterns:
State Reading
Reading state is thread-safe for individual operations, but when multiple related states need to be read together, use a critical section:
// Example of safe multi-state reading
TaskHandle_t current_task = xTaskGetCurrentTaskHandle();
vTaskSuspendAll(); // Prevent context switches
{
bool is_off_hook = ma_bell_state_phone_bits_set(PHONE_STATE_OFF_HOOK);
bool is_ringing = ma_bell_state_phone_bits_set(PHONE_STATE_RINGING);
// Use both values together...
}
xTaskResumeAll();
State Updates
State updates are not atomic - use FreeRTOS primitives for synchronization. For coordinated updates, use a mutex:
// Example of safe state updates
static SemaphoreHandle_t state_mutex = NULL;
void update_phone_state(void) {
if (xSemaphoreTake(state_mutex, portMAX_DELAY) == pdTRUE) {
// Update multiple related states
ma_bell_state_update_phone_bits(PHONE_STATE_OFF_HOOK, 0);
ma_bell_state_update_phone_bits(0, PHONE_STATE_RINGING);
xSemaphoreGive(state_mutex);
}
}
Notification Handling
Notifications are delivered asynchronously. Handle them promptly to prevent task starvation and use timeouts to prevent deadlocks:
// Example of safe notification handling
while (1) {
uint32_t notification = ma_bell_state_wait_for_notification(
NOTIFY_PHONE_STATE_CHANGED | NOTIFY_BT_STATE_CHANGED,
pdMS_TO_TICKS(1000) // 1 second timeout
);
if (notification == 0) {
// Handle timeout
continue;
}
// Process notifications
if (notification & NOTIFY_PHONE_STATE_CHANGED) {
// Handle phone state change
}
}
Critical Sections
Keep critical sections as short as possible
Don’t call blocking functions inside critical sections
Consider using task notifications for synchronization
Common Pitfalls
Don’t assume state hasn’t changed between reads
Don’t hold locks while waiting for notifications
Don’t update state from ISRs (use task notifications)
Don’t block in notification handlers
ISR Safety
State updates from ISRs should use task notifications:
// In ISR
BaseType_t xHigherPriorityTaskWoken = pdFALSE;
vTaskNotifyGiveFromISR(state_update_task, &xHigherPriorityTaskWoken);
portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
// In task
void state_update_task(void *pvParameters) {
while (1) {
if (ulTaskNotifyTake(pdTRUE, portMAX_DELAY)) {
// Safe to update state here
ma_bell_state_update_phone_bits(PHONE_STATE_OFF_HOOK, 0);
}
}
}
Best Practices
Always initialize the state engine before use
Use the provided bit manipulation functions instead of direct access
Register for notifications early in task initialization
Handle state changes promptly to maintain system responsiveness
Use appropriate timeouts when waiting for notifications
Keep critical sections as short as possible
Use mutexes for coordinated state updates
Handle ISR state updates through task notifications