// Copyright (c) 2017-2024, Mudita Sp. z.o.o. All rights reserved. // For licensing, see https://github.com/mudita/MuditaOS/blob/master/LICENSE.md #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace antenna { const char *c_str(antenna::State state) { switch (state) { case antenna::State::none: return "none"; case antenna::State::init: return "init"; case antenna::State::connectionStatus: return "connectionStatus"; case antenna::State::switchAntenna: return "switchAntenna"; case antenna::State::signalCheck: return "signalCheck"; case antenna::State::bandCheck: return "bandCheck"; case antenna::State::idle: return "idle"; case antenna::State::csqChange: return "csqChange"; case antenna::State::locked: return "locked"; } return ""; } } // namespace antenna inline constexpr auto antennaServiceStackSize = 1024 * 2; ServiceAntenna::ServiceAntenna() : sys::Service(service::name::antenna, "", antennaServiceStackSize, sys::ServicePriority::Idle), state{std::make_unique>(this)}, phoneModeObserver{ std::make_unique()} { timer = sys::TimerFactory::createPeriodicTimer( this, "Antenna", std::chrono::seconds{5}, [this](sys::Timer & /*timer*/) { timer.stop(); auto stateToSet = state->get(); if (state->timeoutOccured(cpp_freertos::Ticks::GetTicks())) { LOG_WARN("State [ %s ] timeout occurred, setting [ %s ] state", c_str(state->get()), c_str(state->getTimeoutState())); stateToSet = state->getTimeoutState(); } state->set(stateToSet); }); bus.channels.push_back(sys::BusChannel::ServiceCellularNotifications); bus.channels.push_back(sys::BusChannel::AntennaNotifications); bus.channels.push_back(sys::BusChannel::PhoneModeChanges); phoneModeObserver->connect(this); phoneModeObserver->subscribe([this](sys::phone_modes::PhoneMode mode) { if (mode == sys::phone_modes::PhoneMode::Offline) { handleLockRequest(antenna::lockState::locked); } else { handleLockRequest(antenna::lockState::unlocked); } }); } ServiceAntenna::~ServiceAntenna() { } // Invoked upon receiving data message sys::MessagePointer ServiceAntenna::DataReceivedHandler(sys::DataMessage *msgl, sys::ResponseMessage *resp) { bool handled = false; switch (msgl->messageType) { case MessageType::StateChange: HandleStateChange(state->get()); break; case MessageType::AntennaChanged: { bsp::cellular::antenna antenna; if (CellularServiceAPI::GetAntenna(this, antenna)) { currentAntenna = antenna; if (state->get() == antenna::State::switchAntenna) { LOG_INFO("Antenna switched"); state->enableStateTimeout(cpp_freertos::Ticks::GetTicks(), pdMS_TO_TICKS(antenna::connectionStatusTimeout), antenna::State::switchAntenna); state->set(antenna::State::connectionStatus); } } handled = true; break; } case MessageType::AntennaLockService: { auto msg = dynamic_cast(msgl); if (msg != nullptr) { handleLockRequest(msg->request); handled = true; } } break; case MessageType::AntennaGetLockState: { auto responseMessage = std::make_shared(true, serviceLocked); return responseMessage; } break; default: break; } if (handled) { return std::make_shared(); } return std::make_shared(sys::ReturnCodes::Unresolved); } // Invoked during initialization sys::ReturnCodes ServiceAntenna::InitHandler() { registerMessageHandlers(); LOG_INFO("Initialized"); return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceAntenna::DeinitHandler() { LOG_INFO("Deinitialized"); return sys::ReturnCodes::Success; } sys::ReturnCodes ServiceAntenna::SwitchPowerModeHandler(const sys::ServicePowerMode mode) { LOG_INFO("New power mode: %s", c_str(mode)); suspended = true; switch (mode) { case sys::ServicePowerMode::Active: suspended = false; break; case sys::ServicePowerMode::SuspendToRAM: case sys::ServicePowerMode::SuspendToNVM: break; } return sys::ReturnCodes::Success; } void ServiceAntenna::handleLockRequest(antenna::lockState request) { auto currentState = state->get(); LOG_INFO("Current state: %s, requested stated: %s", magic_enum::enum_name(currentState).data(), magic_enum::enum_name(request).data()); if (request == antenna::lockState::locked) { if (currentState != antenna::State::locked) { state->set(antenna::State::locked); serviceLocked = request; return; } } else if (request == antenna::lockState::unlocked) { if (currentState == antenna::State::locked) { state->set(state->getLast()); serviceLocked = request; return; } } } bool ServiceAntenna::HandleStateChange(antenna::State state) { bool ret = false; switch (state) { case antenna::State::none: ret = noneStateHandler(); break; case antenna::State::init: ret = initStateHandler(); break; case antenna::State::connectionStatus: ret = connectionStatusStateHandler(); break; case antenna::State::switchAntenna: ret = switchAntennaStateHandler(); break; case antenna::State::signalCheck: ret = signalCheckStateHandler(); break; case antenna::State::bandCheck: ret = bandCheckStateHandler(); break; case antenna::State::idle: ret = idleStateHandler(); break; case antenna::State::csqChange: ret = csqChangeStateHandler(); break; case antenna::State::locked: ret = lockedStateHandler(); break; } if (!ret) { LOG_WARN("State [ %s ] not handled. Reloading timer.", c_str(state)); timer.start(); } return ret; } bool ServiceAntenna::initStateHandler(void) { LOG_INFO("State Init"); bsp::cellular::antenna antenna; if (phoneModeObserver->isInMode(sys::phone_modes::PhoneMode::Offline)) { auto message = std::make_shared(MessageType::AntennaLockService, antenna::lockState::locked); bus.sendUnicast(std::move(message), service::name::antenna); return true; } if (CellularServiceAPI::GetAntenna(this, antenna)) { currentAntenna = antenna; state->enableStateTimeout(cpp_freertos::Ticks::GetTicks(), pdMS_TO_TICKS(antenna::connectionStatusTimeout), antenna::State::switchAntenna); state->set(antenna::State::connectionStatus); return true; } return false; } bool ServiceAntenna::noneStateHandler(void) { return true; } bool ServiceAntenna::connectionStatusStateHandler(void) { auto status = Store::GSM::get()->getNetwork().status; if (status == Store::Network::Status::RegisteredHomeNetwork || status == Store::Network::Status::RegisteredRoaming) { LOG_INFO("Modem connected."); state->disableTimeout(); state->set(antenna::State::bandCheck); return true; } LOG_INFO("Modem not connected."); return false; } bool ServiceAntenna::switchAntennaStateHandler(void) { if (currentAntenna == bsp::cellular::antenna::highBand) { CellularServiceAPI::SelectAntenna(this, bsp::cellular::antenna::lowBand); return true; } else if (currentAntenna == bsp::cellular::antenna::lowBand) { CellularServiceAPI::SelectAntenna(this, bsp::cellular::antenna::highBand); return true; } return false; } bool ServiceAntenna::signalCheckStateHandler(void) { if (currentCsq <= antenna::signalTreshold) { LOG_INFO("Signal strength below the threshold (CSQ = %" PRIu32 ", Threshold = %" PRIu32 "). Switch antenna", currentCsq, antenna::signalTreshold); state->set(antenna::State::switchAntenna); return true; } lastCsq = currentCsq; state->set(antenna::State::idle); return true; } bool ServiceAntenna::bandCheckStateHandler(void) { std::string cellularResponse; if (CellularServiceAPI::GetQNWINFO(this, cellularResponse)) { std::string qnwinfo; at::response::parseQNWINFO(cellularResponse, qnwinfo); LOG_INFO("QNWINFO: %s", qnwinfo.c_str()); const auto bandFrequency = at::response::qnwinfo::parseNetworkFrequency(qnwinfo); LOG_INFO("Band frequency: %" PRIu32, bandFrequency); constexpr uint32_t GHz = 1000; const bool isBandSubGHz = bandFrequency < GHz; if (currentAntenna == bsp::cellular::antenna::highBand) { LOG_INFO("High band antenna."); if (isBandSubGHz) { LOG_INFO("Modem connected on sub GHz band."); state->set(antenna::State::signalCheck); return true; } } if (currentAntenna == bsp::cellular::antenna::lowBand) { LOG_INFO("Low band antenna."); if (!isBandSubGHz) { LOG_INFO("Modem connected on over GHz band."); state->set(antenna::State::signalCheck); return true; } } LOG_INFO("Band match."); state->set(antenna::State::idle); return true; } return false; } bool ServiceAntenna::idleStateHandler(void) { return true; } bool ServiceAntenna::csqChangeStateHandler(void) { constexpr uint32_t notValidCsq = 99; auto nextState = antenna::State::idle; if (lastCsq != currentCsq || currentCsq == notValidCsq) { LOG_INFO("CSQ change occurrence, check connection."); nextState = antenna::State::connectionStatus; } if (currentCsq != notValidCsq) { lastCsq = currentCsq; } LOG_INFO("CSQ value: %" PRIu32, currentCsq); state->set(nextState); return true; } bool ServiceAntenna::lockedStateHandler(void) { state->disableTimeout(); return true; } void ServiceAntenna::registerMessageHandlers() { connect(typeid(antenna::message::InvalidCsqNotification), [&](sys::Message *request) -> sys::MessagePointer { if (state->get() == antenna::State::idle) { state->set(antenna::State::connectionStatus); } return sys::MessageNone{}; }); connect(typeid(cellular::msg::notification::SimReady), [this](sys::Message *) { state->set(antenna::State::init); return sys::MessageNone{}; }); connect(typeid(antenna::message::InvalidCsqNotification), [&](sys::Message *request) -> sys::MessagePointer { if (state->get() == antenna::State::idle) { state->set(antenna::State::connectionStatus); } return sys::MessageNone{}; }); connect(typeid(cellular::SignalStrengthUpdateNotification), [&](sys::Message *request) -> sys::MessagePointer { currentCsq = Store::GSM::get()->getSignalStrength().rssi; if (state->get() == antenna::State::idle) { state->set(antenna::State::csqChange); } return sys::MessageNone{}; }); connect(typeid(cellular::IncomingCallMessage), [&](sys::Message *request) -> sys::MessagePointer { auto message = std::make_shared(MessageType::AntennaLockService, antenna::lockState::locked); bus.sendUnicast(std::move(message), service::name::antenna); return sys::MessageNone{}; }); connect(typeid(cellular::RingingMessage), [&](sys::Message *request) -> sys::MessagePointer { auto message = std::make_shared(MessageType::AntennaLockService, antenna::lockState::locked); bus.sendUnicast(std::move(message), service::name::antenna); return sys::MessageNone{}; }); connect(typeid(cellular::HangupCallMessage), [&](sys::Message *request) -> sys::MessagePointer { auto message = std::make_shared(MessageType::AntennaLockService, antenna::lockState::unlocked); bus.sendUnicast(std::move(message), service::name::antenna); return sys::MessageNone{}; }); connect(typeid(cellular::CallAbortedNotification), [&](sys::Message *request) -> sys::MessagePointer { auto message = std::make_shared(MessageType::AntennaLockService, antenna::lockState::unlocked); bus.sendUnicast(std::move(message), service::name::antenna); return sys::MessageNone{}; }); }