~aleteoryx/muditaos

c5e533f94f68cd4e2924e8d3d3e485612214082f — Marcin Zieliński 3 years ago 8154243
[MOS-770] Replace VoLTE state switching mechanism

Introduced the new implementation.
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