/* * 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 "accessory_manager.h" #include "accessory_idle_mode.h" #include "accessory_imaging.h" #include "accessory_mfg_mode.h" #include "drivers/accessory.h" #include "system/logging.h" #include "os/mutex.h" static AccessoryInputState s_input_state = AccessoryInputStateIdle; static PebbleMutex *s_state_mutex; void accessory_manager_init(void) { s_state_mutex = mutex_create(); } bool accessory_manager_handle_character_from_isr(char c) { // NOTE: THIS IS RUN WITHIN AN ISR switch (s_input_state) { case AccessoryInputStateMfg: return accessory_mfg_mode_handle_char(c); case AccessoryInputStateIdle: return accessory_idle_mode_handle_char(c); case AccessoryInputStateImaging: return accessory_imaging_handle_char(c); case AccessoryInputStateMic: // fallthrough default: break; } return false; } bool accessory_manager_handle_break_from_isr(void) { // NOTE: THIS IS RUN WITHIN AN ISR switch (s_input_state) { case AccessoryInputStateIdle: case AccessoryInputStateMic: case AccessoryInputStateMfg: case AccessoryInputStateImaging: // fallthrough default: break; } return false; } // Valid state transitions are: // +-----+ // | IMG | // +-----+ // ^ // | // v // +------+ +-----+ +-----+ // | Idle |<-->| MFG |<-->| MIC | // +------+ +-----+ +-----+ static bool prv_is_valid_state_transition(AccessoryInputState new_state) { if (s_input_state == AccessoryInputStateIdle) { return new_state == AccessoryInputStateMfg; } else if (s_input_state == AccessoryInputStateMfg) { return (new_state == AccessoryInputStateIdle) || (new_state == AccessoryInputStateImaging) || (new_state == AccessoryInputStateMic); } else if (s_input_state == AccessoryInputStateImaging) { return new_state == AccessoryInputStateMfg; } else if (s_input_state == AccessoryInputStateMic) { return new_state == AccessoryInputStateMfg; } return false; } // The accessory state is used to differentiate between different consumers of the accessory port. // Before a consumer uses the accessory port, it must set its state and return the state to idle // once it has finished. No other consumer will be permitted to use the accessory port until the // state is returned to idle. bool accessory_manager_set_state(AccessoryInputState state) { mutex_lock(s_state_mutex); if (!prv_is_valid_state_transition(state)) { // the state is already set by somebody else mutex_unlock(s_state_mutex); return false; } s_input_state = state; switch (s_input_state) { case AccessoryInputStateMfg: accessory_enable_input(); accessory_set_baudrate(AccessoryBaud115200); accessory_set_power(false); accessory_mfg_mode_start(); break; case AccessoryInputStateIdle: // restore accessory to default state accessory_enable_input(); accessory_set_baudrate(AccessoryBaud115200); accessory_set_power(false); break; case AccessoryInputStateImaging: accessory_enable_input(); accessory_set_baudrate(AccessoryBaud921600); accessory_set_power(false); break; case AccessoryInputStateMic: // fallthrough default: break; } mutex_unlock(s_state_mutex); PBL_LOG(LOG_LEVEL_DEBUG, "Setting accessory state to %u", state); return true; }