M module-apps/CMakeLists.txt => module-apps/CMakeLists.txt +5 -0
@@ 58,6 58,11 @@ if (${CMAKE_BUILD_TYPE} STREQUAL "Debug")
endforeach()
endif()
+option(ENABLE_VOLTE "Enable VoLTE" OFF)
+if(${ENABLE_VOLTE})
+ target_compile_definitions(${PROJECT_NAME} PUBLIC ENABLE_VOLTE=1)
+endif()
+
option(DISABLED_SETTINGS_OPTIONS "Enable Disabled Settings Options" OFF)
if(${DISABLED_SETTINGS_OPTIONS})
target_compile_definitions(${PROJECT_NAME} PUBLIC DISABLED_SETTINGS_OPTIONS=1)
M module-services/service-cellular/CMakeLists.txt => module-services/service-cellular/CMakeLists.txt +1 -0
@@ 13,6 13,7 @@ set(SOURCES
src/TerhetingHandler.cpp
src/ModemResetHandler.cpp
src/URCCounter.cpp
+ src/VolteHandlerImpl.cpp
src/CSQHandler.cpp
DTMFCode.cpp
M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +13 -1
@@ 870,7 870,19 @@ bool ServiceCellular::handle_cellular_priv_init()
priv->simContacts->setChannel(channel);
priv->imeiGetHandler->setChannel(channel);
- if (!priv->tetheringHandler->configure()) {
+#if ENABLE_VOLTE == 1
+ constexpr bool enableVolte = true;
+#else
+ constexpr bool enableVolte = false;
+#endif
+ bool needReset = false;
+ try {
+ needReset = !priv->tetheringHandler->configure() || !priv->volteHandler->switchVolte(*channel, enableVolte);
+ }
+ catch (std::runtime_error const &exc) {
+ LOG_ERROR("%s", exc.what());
+ }
+ if (needReset) {
priv->modemResetHandler->performHardReset();
return true;
}
M module-services/service-cellular/src/ServiceCellularPriv.hpp => module-services/service-cellular/src/ServiceCellularPriv.hpp +4 -0
@@ 12,6 12,8 @@
#include "SimContacts.hpp"
#include "ImeiGetHandler.hpp"
#include "TetheringHandler.hpp"
+#include "VolteHandler.hpp"
+#include "VolteHandlerImpl.hpp"
#include "ModemResetHandler.hpp"
#include "CSQHandler.hpp"
@@ 24,6 26,7 @@ namespace cellular::internal
using service::SimContacts;
using service::State;
using service::TetheringHandler;
+ using service::VolteHandler;
class ServiceCellularPriv
{
@@ 36,6 39,7 @@ namespace cellular::internal
std::unique_ptr<SimContacts> simContacts;
std::unique_ptr<service::ImeiGetHandler> imeiGetHandler;
std::unique_ptr<TetheringHandler> tetheringHandler;
+ std::unique_ptr<VolteHandler<DLCChannel, ModemResponseParserImpl>> volteHandler;
std::unique_ptr<ModemResetHandler> modemResetHandler;
std::unique_ptr<CSQHandler> csqHandler;
A module-services/service-cellular/src/VolteHandler.hpp => module-services/service-cellular/src/VolteHandler.hpp +88 -0
@@ 0,0 1,88 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include <module-cellular/at/ATFactory.hpp>
+#include <module-cellular/at/response.hpp>
+#include <module-utils/utility/Utils.hpp>
+#include <stdexcept>
+#include <string_view>
+
+namespace
+{
+ struct NonCopyable
+ {
+ explicit NonCopyable() = default;
+ ~NonCopyable() = default;
+ NonCopyable(NonCopyable const &) = delete;
+ NonCopyable &operator=(NonCopyable const &) = delete;
+ NonCopyable(NonCopyable &&) = default;
+ NonCopyable &operator=(NonCopyable &&) = default;
+ };
+} // namespace
+
+namespace cellular::service
+{
+ using namespace at;
+
+ struct QcfgImsResult : Result
+ {
+ QcfgImsResult(Result &rhs) : Result{std::move(rhs)}
+ {}
+ };
+
+ template <typename CmuxChannel, typename ModemResponseParser>
+ struct VolteHandler : private NonCopyable
+ {
+ bool switchVolte(CmuxChannel &channel, bool enable) const
+ {
+ ModemResponseParser const parser;
+
+ if (enable) {
+ // according to Quectel, this setting doesn't have to be reset when disabling
+ constexpr std::uint8_t voiceDomainPacketSwitchedPreferred = 0x03;
+ auto voiceDomainAnswer =
+ channel.cmd(factory(at::AT::QNVFW) + "\"/nv/item_files/modem/mmode/voice_domain_pref\"," +
+ utils::byteToHex<std::uint8_t>(voiceDomainPacketSwitchedPreferred));
+ if (!voiceDomainAnswer) {
+ throw std::runtime_error("[VoLTE] failed to set voice domain before trying to enable VoLTE");
+ }
+
+ // can be left as always on: doesn't disturb when VoLTE disabled
+ auto qmbnAnswer =
+ channel.cmd(factory(at::AT::QMBNCFG) + std::string("\"autosel\",1"));
+ if (!qmbnAnswer) {
+ throw std::runtime_error("[VoLTE] failed to enable MBN auto-select before trying to enable VoLTE");
+ }
+ }
+
+ auto imsCheckAnswer = channel.cmd(factory(AT::QCFG_IMS));
+ bool alreadyConfigured;
+ try {
+ alreadyConfigured = parser(QcfgImsResult{imsCheckAnswer}, enable);
+ }
+ catch (std::runtime_error const &exc) {
+ throw std::runtime_error(std::string("[VoLTE] while checking IMS configuration state: ") + exc.what());
+ }
+
+ if (!alreadyConfigured) {
+ using namespace response::qcfg_ims;
+ auto imsToggleAnswer = channel.cmd(factory(AT::QCFG_IMS) + "," +
+ imsStateToString(enable ? IMSState::Enable : IMSState::Disable));
+ if (!imsToggleAnswer) {
+ throw std::runtime_error("[VoLTE] failed to " + std::string(enable ? "enable" : "disable") +
+ " IMS");
+ }
+ }
+
+ return alreadyConfigured;
+ }
+
+ private:
+ std::string imsStateToString(response::qcfg_ims::IMSState imsState) const
+ {
+ return std::to_string(magic_enum::enum_integer(imsState));
+ }
+ };
+} // namespace cellular::service
A module-services/service-cellular/src/VolteHandlerImpl.cpp => module-services/service-cellular/src/VolteHandlerImpl.cpp +28 -0
@@ 0,0 1,28 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#include "VolteHandlerImpl.hpp"
+
+namespace cellular::internal
+{
+ bool ModemResponseParserImpl::operator()(QcfgImsResult const &response, bool requestedState) const
+ {
+ using namespace at::response::qcfg_ims;
+
+ if (!response) {
+ throw std::runtime_error("[VoLTE] modem responded with error to QCFG_IMS");
+ }
+
+ std::pair<IMSState, VoLTEIMSState> parsed;
+ if (!at::response::parseQCFG_IMS(response, parsed)) {
+ throw std::runtime_error("[VoLTE] unable to parse modem's response to QCFG_IMS");
+ }
+
+ auto const &ims = parsed.first;
+ bool stateMatchesRequest = (ims != IMSState::Disable) == requestedState;
+ LOG_INFO("[VoLTE] current IMS state: %s which %s expectation",
+ magic_enum::enum_name(ims).data(),
+ ((stateMatchesRequest) ? "matches" : "doesn't match"));
+ return stateMatchesRequest;
+ }
+} // namespace cellular::internal
A module-services/service-cellular/src/VolteHandlerImpl.hpp => module-services/service-cellular/src/VolteHandlerImpl.hpp +17 -0
@@ 0,0 1,17 @@
+// Copyright (c) 2017-2022, Mudita Sp. z.o.o. All rights reserved.
+// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md
+
+#pragma once
+
+#include "VolteHandler.hpp"
+#include <at/response.hpp>
+
+namespace cellular::internal
+{
+ using namespace cellular::service;
+
+ struct ModemResponseParserImpl
+ {
+ bool operator()(QcfgImsResult const &, bool) const;
+ };
+} // namespace cellular::internal