~aleteoryx/muditaos

ea27e6871615172000db8664a9d11d1b6103c562 — Maciej Janicki 5 years ago 6b3fdbc
[EGD-5748] Remake Cellular flow

This commit remakes cellular data flow
introducing full error propagation.
43 files changed, 1638 insertions(+), 866 deletions(-)

M enabled_unittests
M module-bsp/board/linux/cellular/linux_cellular.cpp
M module-bsp/board/linux/cellular/linux_cellular.hpp
M module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.cpp
M module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.hpp
M module-bsp/board/rt1051/common/irq/irq_gpio.cpp
A module-bsp/bsp/cellular/CellularResult.hpp
M module-bsp/bsp/cellular/bsp_cellular.cpp
M module-bsp/bsp/cellular/bsp_cellular.hpp
M module-cellular/Modem/ATCommon.cpp
M module-cellular/Modem/ATCommon.hpp
M module-cellular/Modem/ATParser.cpp
M module-cellular/Modem/ATParser.hpp
M module-cellular/Modem/BaseChannel.hpp
A module-cellular/Modem/README.md
M module-cellular/Modem/TS0710/DLC_channel.cpp
M module-cellular/Modem/TS0710/DLC_channel.h
M module-cellular/Modem/TS0710/TS0710.cpp
M module-cellular/Modem/TS0710/TS0710.h
M module-cellular/Modem/TS0710/TS0710_DATA.cpp
M module-cellular/Modem/TS0710/TS0710_DLC_ESTABL.cpp
M module-cellular/Modem/TS0710/TS0710_Frame.h
A module-cellular/Modem/doc/Images/at_mode.svg
A module-cellular/Modem/doc/Images/at_mode.uml
A module-cellular/Modem/doc/Images/cellular_mux_read.png
A module-cellular/Modem/doc/Images/cellular_result_struct.png
A module-cellular/Modem/doc/Images/cellular_result_struct.uml
A module-cellular/Modem/doc/Images/cmx_mode.uml
A module-cellular/Modem/doc/Images/dma_result_struct.png
A module-cellular/Modem/doc/Images/dma_result_struct.uml
A module-cellular/Modem/doc/Images/mux_mode.svg
A module-cellular/Modem/doc/Images/single_cmd.uml
A module-cellular/Modem/doc/Images/single_cmd_transmission.png
M module-cellular/at/Result.hpp
M module-cellular/at/src/ATFactory.cpp
M module-cellular/test/CMakeLists.txt
M module-cellular/test/mock/AtCommon_channel.hpp
A module-cellular/test/unittest_CellularResult.cpp
M module-cellular/test/unittest_cmux.cpp
M module-services/service-cellular/ServiceCellular.cpp
M module-services/service-evtmgr/WorkerEvent.cpp
M module-utils/log/Logger.cpp
M module-utils/log/debug.hpp
M enabled_unittests => enabled_unittests +5 -0
@@ 473,4 473,9 @@ TESTS_LIST["catch2-audio-volume-scaler"]="
    Scenario: Scale volume levels between system and bluetooth;
"
#---------
TESTS_LIST["catch2-unittest_CellularResult"]="
    CellularResult;
"
#---------



M module-bsp/board/linux/cellular/linux_cellular.cpp => module-bsp/board/linux/cellular/linux_cellular.cpp +21 -24
@@ 12,10 12,8 @@
#include <errno.h>
#include <fcntl.h>
#include <map>
#include <sys/stat.h>
#include <sys/types.h>
#include <ticks.hpp>
#include <time.h>
#include <unistd.h>

#define _LINUX_UART_DEBUG 0


@@ 68,8 66,6 @@ namespace bsp
            LOG_FATAL("Failed to create epoll file descriptor");
        }

        struct epoll_event event;

        event.events = EPOLLIN;

        if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &event)) {


@@ 91,23 87,23 @@ namespace bsp
        }
    }

    void LinuxCellular::PowerUp()
    void LinuxCellular::powerUp()
    {}

    void LinuxCellular::PowerDown()
    void LinuxCellular::powerDown()
    {}

    void LinuxCellular::Restart()
    void LinuxCellular::restart()
    {}

    ssize_t LinuxCellular::Read(void *buf, size_t nbytes)
    ssize_t LinuxCellular::read(void *buf, size_t nbytes, std::chrono::milliseconds timeoutMs)
    {

        cpp_freertos::LockGuard lock(serOutMutex);

        int ret;
        for (;;) {
            ret = read(fd, buf, nbytes);
            ret = ::read(fd, buf, nbytes);
            if (ret != -1 || errno != EINTR)
                break;
        }


@@ 124,7 120,7 @@ namespace bsp
        return ret;
    }

    ssize_t LinuxCellular::Write(void *buf, size_t nbytes)
    ssize_t LinuxCellular::write(void *buf, size_t nbytes)
    {
        cpp_freertos::LockGuard lock(serOutMutex);
#if _LINUX_UART_DEBUG


@@ 136,30 132,31 @@ namespace bsp
#endif
        int ret;
        for (;;) {
            ret = write(fd, buf, nbytes);
            ret = ::write(fd, buf, nbytes);
            if (ret != -1 || errno != EINTR)
                break;
        }
        return ret;
    }

    void LinuxCellular::InformModemHostAsleep(void)
    void LinuxCellular::informModemHostAsleep(void)
    {}

    void LinuxCellular::InformModemHostWakeup(void)
    void LinuxCellular::informModemHostWakeup(void)
    {}

    void LinuxCellular::EnterSleep()
    void LinuxCellular::enterSleep()
    {}

    void LinuxCellular::ExitSleep()
    void LinuxCellular::exitSleep()
    {}

    uint32_t LinuxCellular::Wait(uint32_t timeout)
    uint32_t LinuxCellular::wait(std::chrono::milliseconds timeoutMs)
    {
        auto timeoutTicks = pdMS_TO_TICKS(timeoutMs.count());

        uint32_t currentTime   = cpp_freertos::Ticks::GetTicks();
        uint32_t timeoutNeeded = timeout == UINT32_MAX ? UINT32_MAX : currentTime + timeout;
        uint32_t timeoutNeeded = currentTime + timeoutTicks;
        uint32_t timeElapsed   = currentTime;

        for (;;) {


@@ 182,7 179,7 @@ namespace bsp
        }
    }

    void LinuxCellular::SetSpeed(uint32_t portSpeed)
    void LinuxCellular::setSpeed(uint32_t portSpeed)
    {
        struct termios t;
        memset(&t, 0, sizeof(t));


@@ 201,10 198,10 @@ namespace bsp
        ioctl(fd, TIOCMBIS, &status);
    }

    void LinuxCellular::SelectAntenna(bsp::cellular::antenna antenna)
    void LinuxCellular::selectAntenna(bsp::cellular::antenna antenna)
    {}

    bsp::cellular::antenna LinuxCellular::GetAntenna()
    bsp::cellular::antenna LinuxCellular::getAntenna()
    {
        return bsp::cellular::antenna::lowBand;
    }


@@ 248,7 245,7 @@ namespace bsp
        namespace sim
        {

            auto trayIRQ_handler() -> BaseType_t
            auto trayIRQHandler() -> BaseType_t
            {
                return BaseType_t();
            }


@@ 258,15 255,15 @@ namespace bsp
                return Store::GSM::Tray::IN;
            }

            void hotswap_trigger()
            void hotSwapTrigger()
            {}

            void sim_sel()
            void simSelect()
            {}
        } // namespace sim
        namespace ringIndicator
        {
            auto riIRQ_handler() -> BaseType_t
            auto riIRQHandler() -> BaseType_t
            {
                return BaseType_t();
            }

M module-bsp/board/linux/cellular/linux_cellular.hpp => module-bsp/board/linux/cellular/linux_cellular.hpp +15 -18
@@ 14,7 14,6 @@

namespace bsp
{

    class LinuxCellular : public Cellular
    {
      private:


@@ 24,40 23,40 @@ namespace bsp
        LinuxCellular(const char *term, uint32_t portSpeed);
        ~LinuxCellular();

        void PowerUp() override final;
        void powerUp() override final;

        void PowerDown() override final;
        void powerDown() override final;

        void Restart() override final;
        void restart() override final;

        uint32_t Wait(uint32_t timeout) override final;
        uint32_t wait(std::chrono::milliseconds timeoutMs) override final;

        ssize_t Read(void *buf, size_t nbytes) override final;
        ssize_t read(void *buf, size_t nbytes, std::chrono::milliseconds timeoutMs) override final;

        ssize_t Write(void *buf, size_t nbytes) override final;
        ssize_t write(void *buf, size_t nbytes) override final;

        void InformModemHostAsleep() override final;
        void informModemHostAsleep() override final;

        void InformModemHostWakeup() override final;
        void informModemHostWakeup() override final;

        void EnterSleep() override final;
        void enterSleep() override final;

        void ExitSleep() override final;
        void exitSleep() override final;

        void SetSpeed(uint32_t portSpeed);
        void setSpeed(uint32_t portSpeed) override final;

        void SetSendingAllowed(bool state) override final
        void setSendingAllowed(bool state) override final
        {
            pv_SendingAllowed = state;
        }
        bool GetSendingAllowed() override final
        bool getSendingAllowed() const noexcept override final
        {
            return pv_SendingAllowed;
        }

        void SelectAntenna(bsp::cellular::antenna antenna) override final;
        void selectAntenna(bsp::cellular::antenna antenna) override final;

        bsp::cellular::antenna GetAntenna() override final;
        bsp::cellular::antenna getAntenna() override final;

      private:
        static constexpr speed_t baud_bits[] = {0,


@@ 74,8 73,6 @@ namespace bsp
                                                B3000000,
                                                B4000000};

        static const uint32_t portBaudRate = 115200;

        static const uint32_t MAX_EVENTS = 1;

        int fd = -1;

M module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.cpp => module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.cpp +176 -194
@@ 2,93 2,81 @@
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#include "rt1051_cellular.hpp"
#include "FreeRTOS.h"
#include "task.h"
#include "stream_buffer.h"

#include "dma_config.h"
#include "fsl_cache.h"

#include <common_data/EventStore.hpp>
#include <task.h>
#include <ticks.hpp>

#include <algorithm>

#if _RT1051_UART_DEBUG == 1
static bsp::cellular::CellularDMAResultStruct RXfer;

#if DEBUG_CELLULAR_UART == 1
#define logUARTdebug(...) LOG_DEBUG(__VA_ARGS__)
#else
#define logUARTdebug(...)
#endif

namespace bsp::cellular
{
    void notifyReceivedNew()
    {
        BaseType_t hp = pdFALSE;
        if (bsp::RT1051Cellular::untilReceivedNewHandle != nullptr) {
            vTaskNotifyGiveFromISR(bsp::RT1051Cellular::untilReceivedNewHandle, &hp);
        }
        portEND_SWITCHING_ISR(hp);
    }
}; // namespace bsp::cellular

extern "C"
{
    void LPUART1_IRQHandler(void)
    {
        uint32_t isrReg = LPUART_GetStatusFlags(CELLULAR_UART_BASE);
        std::uint32_t isrReg = LPUART_GetStatusFlags(CELLULAR_UART_BASE);

        if (bsp::RT1051Cellular::uartRxStreamBuffer != NULL) {
            auto RxDmaStatus = LPUART_TransferGetReceiveCountEDMA(
                CELLULAR_UART_BASE,
                &bsp::RT1051Cellular::uartDmaHandle,
                reinterpret_cast<uint32_t *>(&bsp::RT1051Cellular::RXdmaReceivedCount));
        if (bsp::RT1051Cellular::uartRxBuffer != NULL) {
            std::uint32_t count = 0;

            if (isrReg & bsp::RT1051Cellular::startIRQMask) {
            auto RxDmaStatus =
                LPUART_TransferGetReceiveCountEDMA(CELLULAR_UART_BASE, &bsp::RT1051Cellular::uartDmaHandle, &count);

            RXfer.dataSize = count;

            if ((isrReg & bsp::RT1051Cellular::edgeIRQMask) != 0u ||
                (isrReg & bsp::RT1051Cellular::dataRegFullIRQMask) != 0) {
                // logUARTdebug("[RX] edgeIRQ");
                if (RxDmaStatus == kStatus_NoTransferInProgress) {
                    LPUART_DisableInterrupts(CELLULAR_UART_BASE, bsp::RT1051Cellular::startIRQMaskEnable);
                    bsp::RT1051Cellular::RXdmaReceivedCount = -1;
                    if (not bsp::RT1051Cellular::StartReceive(bsp::RT1051Cellular::GetFreeStreamBufferSize())) {
                    // logUARTdebug("[RX] No transfer in progress");
                    LPUART_DisableInterrupts(CELLULAR_UART_BASE, bsp::RT1051Cellular::edgeIRQMaskEnable);

                    if (not bsp::RT1051Cellular::startReceive(bsp::RT1051Cellular::getMaxBufferDataSize())) {
                        bsp::RT1051Cellular::RestartReceivingManually = true;
                        bsp::RT1051Cellular::FinishReceive();
                    }
                    logUARTdebug("[RX] on Incoming data");
                }
            }
            if (isrReg & bsp::RT1051Cellular::finishIRQMask) {

            if ((isrReg & bsp::RT1051Cellular::idleIRQMask) != 0u &&
                (isrReg & bsp::RT1051Cellular::dataRegFullIRQMask) == 0u) {
                // logUARTdebug("[RX] idleIRQ");
                if (RxDmaStatus != kStatus_NoTransferInProgress) {
                    // logUARTdebug("[RX] Transfer in progress. Stopping engine");
                    LPUART_TransferAbortReceiveEDMA(CELLULAR_UART_BASE, &bsp::RT1051Cellular::uartDmaHandle);
                    logUARTdebug("[RX idle] stopped engine");
                }
                logUARTdebug("[RX idle], received %d bytes", bsp::RT1051Cellular::RXdmaReceivedCount);
                // the main exit path on transmission done
                if (bsp::RT1051Cellular::RXdmaReceivedCount >= 0) {
                    if (bsp::RT1051Cellular::RXdmaReceivedCount > 0) {
                        bsp::RT1051Cellular::MoveRxDMAtoStreamBuf(bsp::RT1051Cellular::RXdmaReceivedCount);
                        bsp::RT1051Cellular::RXdmaReceivedCount = -1;
                    }
                    if (not bsp::RT1051Cellular::RestartReceivingManually) {
                        bsp::RT1051Cellular::FinishReceive();

                    if (count > 0) {
                        bsp::RT1051Cellular::sendRxDmaResult(bsp::cellular::CellularResultCode::ReceivedAndIdle);
                    }
                }
            }

            LPUART_ClearStatusFlags(CELLULAR_UART_BASE, isrReg);
            if (isrReg & kLPUART_RxOverrunFlag) {
                LOG_WARN("IRQ RX overrun");
            }
        }

        LPUART_ClearStatusFlags(CELLULAR_UART_BASE, isrReg);
    }
};

namespace bsp
{
    uint8_t RT1051Cellular::RXdmaBuffer[RXdmaBufferSize] = {0};
    ssize_t RT1051Cellular::RXdmaReceivedCount           = -1;
    size_t RT1051Cellular::RXdmaMaxReceivedCount         = -1;
    bool RT1051Cellular::RestartReceivingManually        = false;
    std::size_t RT1051Cellular::RXdmaMaxReceivedCount = 0;
    bool RT1051Cellular::RestartReceivingManually     = false;

    using namespace drivers;

    lpuart_edma_handle_t RT1051Cellular::uartDmaHandle      = {};
    TaskHandle_t RT1051Cellular::untilReceivedNewHandle     = nullptr;
    StreamBufferHandle_t RT1051Cellular::uartRxStreamBuffer = nullptr;
    lpuart_edma_handle_t RT1051Cellular::uartDmaHandle  = {};
    TaskHandle_t RT1051Cellular::untilReceivedNewHandle = nullptr;
    MessageBufferHandle_t RT1051Cellular::uartRxBuffer  = nullptr;

    RT1051Cellular::RT1051Cellular()
    {


@@ 98,9 86,9 @@ namespace bsp
            GPIO_PinRead(GPIO2, BSP_CELLULAR_SIM_TRAY_INSERTED_PIN) == 0 ? Store::GSM::Tray::IN : Store::GSM::Tray::OUT;
        DMAInit();

        uartRxStreamBuffer = xStreamBufferCreate(rxStreamBufferLength, rxStreamBufferNotifyWatermark);
        if (uartRxStreamBuffer == NULL) {
            LOG_ERROR("Could not create the RX stream buffer!");
        uartRxBuffer = xMessageBufferCreate(rxMessageBufferLength);
        if (uartRxBuffer == nullptr) {
            LOG_ERROR("Could not create the RX message buffer!");
            return;
        }



@@ 116,7 104,7 @@ namespace bsp
        s_cellularConfig.parityMode    = kLPUART_ParityDisabled;
        s_cellularConfig.isMsb         = false;
        s_cellularConfig.rxIdleType    = kLPUART_IdleTypeStartBit;
#if _RT1051_UART_DEBUG
#if DEBUG_CELLULAR_UART
        s_cellularConfig.rxIdleConfig = kLPUART_IdleCharacter4; // Logs take time
#else
        s_cellularConfig.rxIdleConfig = kLPUART_IdleCharacter1;


@@ 133,36 121,36 @@ namespace bsp
            return;
        }

        static_assert(rxStreamBufferLength >= 6, "Minimum buffer size (i.e. sufficient to enable flow control)");
        static_assert(rxStreamBufferLength >= 1024, "To be able to fit entire response");

        // CANNOT disable FIFO, dma *needs* it :o
        // CELLULAR_UART_BASE->FIFO &= ~LPUART_FIFO_RXFE_MASK;
        static_assert(rxMessageBufferLength >= RXfer.getMaxSize() + messageBufferOverheadSize,
                      "Minimum buffer size to hold one full DMA rxfer");

        LPUART_ClearStatusFlags(CELLULAR_UART_BASE, 0xFFFFFFFF);
        NVIC_ClearPendingIRQ(LPUART1_IRQn);
        NVIC_SetPriority(LPUART1_IRQn, configLIBRARY_LOWEST_INTERRUPT_PRIORITY);
        NVIC_EnableIRQ(LPUART1_IRQn);

        enableRx();
        isInitialized = true;
    }

    void RT1051Cellular::SetSpeed(uint32_t portSpeed)
    void RT1051Cellular::setSpeed(uint32_t portSpeed)
    {
        LOG_DEBUG("[RT1051] Setting %" PRIu32 " baudrate", portSpeed);

        disableRx();
        LPUART_SetBaudRate(CELLULAR_UART_BASE, portSpeed, GetPerphSourceClock(PerphClock_LPUART));
        enableRx();
    }

    RT1051Cellular::~RT1051Cellular()
    {

        if (uartRxStreamBuffer) {
            vStreamBufferDelete(uartRxStreamBuffer);
            uartRxStreamBuffer = nullptr;
        if (uartRxBuffer) {
            vMessageBufferDelete(uartRxBuffer);
            uartRxBuffer = nullptr;
        }

        DisableRx();
        DisableTx();
        disableRx();
        disableTx();

        NVIC_DisableIRQ(LPUART1_IRQn);
        LPUART_DisableInterrupts(CELLULAR_UART_BASE,


@@ 179,83 167,67 @@ namespace bsp
        untilReceivedNewHandle = nullptr;
    }

    void RT1051Cellular::PowerUp()
    void RT1051Cellular::powerUp()
    {
        const TickType_t POWER_UP_DELAY_MS = 500;
        constexpr TickType_t POWER_UP_DELAY_MS = 500;

        DisableRx();
        disableRx();

        ExitSleep();
        exitSleep();

        TickType_t tick = xTaskGetTickCount();
        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_POWER_PIN), 1);
        vTaskDelayUntil(&tick, POWER_UP_DELAY_MS);
        // BSP_CellularSetPowerState(CellularPowerStateTurningOn);
        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_POWER_PIN), 0);

        EnableRx();
        enableRx();
    }

    void RT1051Cellular::PowerDown()
    void RT1051Cellular::powerDown()
    {
        const uint16_t POWER_DOWN_DELAY_MS = 700;
        constexpr std::uint16_t POWER_DOWN_DELAY_MS = 700;

        ExitSleep();
        exitSleep();

        TickType_t tick = xTaskGetTickCount();
        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_POWER_PIN), 1);
        vTaskDelayUntil(&tick, POWER_DOWN_DELAY_MS);
        // BSP_CellularSetPowerState(CellularPowerStateTurningOff);
        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_POWER_PIN), 0);
        // vTaskDelay(pdMS_TO_TICKS(POWER_DOWN_IN_PROGRESS_DELAY_MS));

        /*        while (s_cellularPowerState != CellularPowerStatePoweredDown)
                {
                    vTaskDelay(pdMS_TO_TICKS(200));
                }*/

        DisableRx();
        disableRx();
    }

    void RT1051Cellular::Restart()
    void RT1051Cellular::restart()
    {
        const uint16_t RESET_DELAY_MS = 460;
        constexpr std::uint16_t RESET_DELAY_MS = 460;

        ExitSleep();
        exitSleep();

        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_RESET_PIN), 1);
        vTaskDelay(pdMS_TO_TICKS(RESET_DELAY_MS));
        gpio_2->WritePin(static_cast<uint32_t>(BoardDefinitions::CELLULAR_GPIO_2_RESET_PIN), 0);
    }

    ssize_t RT1051Cellular::Write(void *buf, size_t nbytes)
    ssize_t RT1051Cellular::write(void *buf, size_t nbytes)
    {
        static uint8_t txBuffer[128];
        memcpy(txBuffer, buf, nbytes);

        lpuart_transfer_t sendXfer;
#if _RT1051_UART_DEBUG
        LOG_PRINTF("[TX: %d]", nbytes);
        uint8_t *ptr = (uint8_t *)buf;
        LOG_PRINTF("\n{");
        for (size_t i = 0; i < nbytes; i++)
            LOG_PRINTF("%02X ", (uint8_t)*ptr++);
        LOG_PRINTF("}\n<");
        ptr = (uint8_t *)buf;
        for (size_t i = 0; i < nbytes; i++)
            LOG_PRINTF("%c", (uint8_t)*ptr++);
        LOG_PRINTF(">");
        LOG_PRINTF("\n");
#endif
        sendXfer.data     = static_cast<uint8_t *>(buf);
        logData("TX", static_cast<uint8_t *>(buf), nbytes);
        sendXfer.data     = txBuffer;
        sendXfer.dataSize = nbytes;

        ExitSleep();
        exitSleep();

        uartDmaHandle.userData = xTaskGetCurrentTaskHandle();
        SCB_CleanInvalidateDCache();

        EnableTx();
        enableTx();

        if (LPUART_SendEDMA(CELLULAR_UART_BASE, &uartDmaHandle, &sendXfer) != kStatus_Success) {
            LOG_ERROR("Cellular: TX Failed!");
            DisableTx();
            disableTx();
            return -1;
        }



@@ 264,58 236,63 @@ namespace bsp

        if (ulNotificationValue == 0) {
            LOG_ERROR("Cellular Uart error: TX Transmission timeout");
            DisableTx();
            disableTx();
            return -1;
        }

        DisableTx();
        disableTx();

        return nbytes;
    }

    bool RT1051Cellular::MoveRxDMAtoStreamBuf(size_t nbytes)
    bool RT1051Cellular::sendRxDmaResult(bsp::cellular::CellularResultCode reason)
    {
        BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        assert(nbytes > 0);

        if (nbytes > GetFreeStreamBufferSize()) {
            LOG_ERROR("Cannot dump DMA buffer. Data is lost (%d>%d)", nbytes, GetFreeStreamBufferSize());
        if (RXfer.getSize() > getMaxBufferDataSize()) {
            LOG_ERROR("Cannot dump DMA buffer (%d>%d)", RXfer.getSize(), getMaxBufferDataSize());
            RestartReceivingManually = true;
            return false;
        }

#if _RT1051_UART_DEBUG
        auto ret =
#endif
            xStreamBufferSendFromISR(uartRxStreamBuffer, (void *)&RXdmaBuffer, nbytes, &xHigherPriorityTaskWoken);
        logUARTdebug("[RX] moved %d bytes to streambuf", ret);
        RXfer.resultCode = reason;
        logUARTdebug("[RX reason] %s", c_str(reason));

        xMessageBufferSendFromISR(uartRxBuffer, (void *)&RXfer, RXfer.getSize(), &xHigherPriorityTaskWoken);

        // logUARTdebug("Added to queue");

        portEND_SWITCHING_ISR(xHigherPriorityTaskWoken);
        return true;
    }

    size_t RT1051Cellular::GetFreeStreamBufferSize()
    size_t RT1051Cellular::getMaxBufferDataSize()
    {
        return xStreamBufferSpacesAvailable(bsp::RT1051Cellular::uartRxStreamBuffer);
        const auto messageOverhead = messageBufferOverheadSize + bsp::cellular::CellularDMAResultStruct::getEmptySize();

        if (const auto bytesFree = xMessageBufferSpaceAvailable(uartRxBuffer); bytesFree > messageOverhead) {
            return bytesFree - messageOverhead;
        }

        return 0;
    }

    bool RT1051Cellular::StartReceive(size_t nbytes)
    bool RT1051Cellular::startReceive(size_t nbytes)
    {
        if (!(GetFreeStreamBufferSize() > 0)) {
            logUARTdebug("Not starting RX DMA, stream buffer is full. Be aware");
        if (getMaxBufferDataSize() <= 0) {
            logUARTdebug("Not starting RX DMA, message buffer is full. Be aware");
            return false;
        }

        else {
            // sanitize input
            RXdmaMaxReceivedCount = std::min(nbytes, static_cast<size_t>(RXdmaBufferSize));
            logUARTdebug("Starting DMA RX, max %d bytes", RXdmaMaxReceivedCount);
            // logUARTdebug("Starting DMA RX, max %d bytes", RXdmaMaxReceivedCount);
        }
        assert(RXdmaMaxReceivedCount <= RXdmaBufferSize);
        RXfer.dataSize = 0;

        // start RXfer if there is a byte incoming and no pending RXfer
        lpuart_transfer_t receiveXfer;
        receiveXfer.data     = RXdmaBuffer;
        receiveXfer.data     = RXfer.data;
        receiveXfer.dataSize = RXdmaMaxReceivedCount;

        // rx config


@@ 333,50 310,43 @@ namespace bsp
        return true;
    }

    void RT1051Cellular::FinishReceive()
    ssize_t RT1051Cellular::read(void *buffer,
                                 size_t nbytes,
                                 std::chrono::milliseconds timeoutMs = std::chrono::milliseconds{0})
    {
        logUARTdebug("[RX] finish");
        bsp::cellular::notifyReceivedNew();
    }
        logUARTdebug("[RX] Read");
        exitSleep();

    ssize_t RT1051Cellular::Read(void *buf, size_t nbytes)
    {
        ExitSleep();
        ssize_t ret = xStreamBufferReceive(uartRxStreamBuffer, buf, nbytes, 0);
#if _RT1051_UART_DEBUG
        if (ret > 0) {
            LOG_PRINTF("[RX: %d]", ret);
            uint8_t *ptr = (uint8_t *)buf;
            LOG_PRINTF("\n{");
            for (ssize_t i = 0; i < ret; i++)
                LOG_PRINTF("%02X ", (uint8_t)*ptr++);
            LOG_PRINTF("}\n<");
            ptr = (uint8_t *)buf;
            for (ssize_t i = 0; i < ret; i++)
                LOG_PRINTF("%c", (uint8_t)*ptr++);
            LOG_PRINTF(">");
        }
        else {
            LOG_PRINTF("[RX] GOT NOTHING (requested %d)", nbytes);
        auto timeoutTicks = pdMS_TO_TICKS(timeoutMs.count());
        if (timeoutTicks > portMAX_DELAY) {
            timeoutTicks = portMAX_DELAY;
        }
        LOG_PRINTF("\n");
#endif
        if (RestartReceivingManually) {
            logUARTdebug("[RX] resume on Paused");
            // need to start manually, as RegBuf might be already full, therefore no Active Edge Interrupt
            if (StartReceive(GetFreeStreamBufferSize())) {
                RestartReceivingManually = false;
            }
            else {
                // do not lose information as whether manual start needs to be initiated and schedule next poke on Read
                LOG_WARN("Modem is waiting to send data. Stream buffer likely too small");
                FinishReceive();

        size_t ret = xMessageBufferReceive(uartRxBuffer, buffer, nbytes, timeoutTicks);

        if (ret > bsp::cellular::CellularDMAResultStruct::getEmptySize()) {
            logData("RX",
                    static_cast<uint8_t *>(buffer) + bsp::cellular::CellularResultStructEmptySize,
                    ret - bsp::cellular::CellularResultStructEmptySize);

            if (RestartReceivingManually) {
                logUARTdebug("[RX] resume on Paused");
                // need to start manually, as RegBuf might be already full, therefore no Active Edge Interrupt
                if (startReceive(getMaxBufferDataSize())) {
                    RestartReceivingManually = false;
                }
                else {
                    // do not lose information as whether manual start needs to be initiated and schedule next poke on
                    // Read
                    LOG_WARN("Modem is waiting to send data. Stream buffer likely too small");
                }
            }
        }

        return ret;
    }

    uint32_t RT1051Cellular::Wait(uint32_t timeout)
    uint32_t RT1051Cellular::wait(std::chrono::milliseconds timeoutMs)
    {
        logUARTdebug("[WAIT]");
        if (untilReceivedNewHandle != nullptr) {


@@ 385,54 355,55 @@ namespace bsp
        }

        // no need to wait: buffer already contains something
        if (xStreamBufferBytesAvailable(uartRxStreamBuffer)) {
        if (!xMessageBufferIsEmpty(uartRxBuffer)) {
            return 1;
        }

        EnableRx();
        enableRx();

        // …start waiting for [timeout] until a new xfer from modem is received
        // start waiting for [timeout] until a new xfer from modem is received
        untilReceivedNewHandle = xTaskGetCurrentTaskHandle();
        auto ret               = ulTaskNotifyTake(pdTRUE, timeout);
        auto ret               = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(timeoutMs.count()));
        untilReceivedNewHandle = nullptr;
        return ret;
    }

    void RT1051Cellular::InformModemHostAsleep()
    void RT1051Cellular::informModemHostAsleep()
    {
        gpio_2->WritePin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_2_APRDY_PIN),
                         !CELLULAR_BSP_AP_READY_PIN_ACTIVE_STATE);
    }

    void RT1051Cellular::InformModemHostWakeup()
    void RT1051Cellular::informModemHostWakeup()
    {
        gpio_2->WritePin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_2_APRDY_PIN),
                         CELLULAR_BSP_AP_READY_PIN_ACTIVE_STATE);
    }

    void RT1051Cellular::EnterSleep()
    void RT1051Cellular::enterSleep()
    {
        if (!isInSleepMode) {
            logUARTdebug("Enter sleep");
            isInSleepMode = true;

            gpio_3->WritePin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_3_DTR_PIN), 1);
            gpio_2->WritePin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_2_WAKEUP_PIN), 1);

            // Host sleep information must be before UART disable
            InformModemHostAsleep();

            informModemHostAsleep();
            if (driverLPUART) {
                driverLPUART->Disable();
            }
        }
    }

    void RT1051Cellular::ExitSleep()
    void RT1051Cellular::exitSleep()
    {
        // reset sleep timer countdown
        lastCommunicationTimestamp = cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks());

        if (isInSleepMode) {
            logUARTdebug("Exit sleep");
            isInSleepMode = false;

            if (driverLPUART) {


@@ 444,7 415,7 @@ namespace bsp
            vTaskDelay(pdMS_TO_TICKS(15));

            // Host wake up information must be after UART enable
            InformModemHostWakeup();
            informModemHostWakeup();
        }
    }



@@ 561,10 532,6 @@ namespace bsp

    void RT1051Cellular::DMAInit()
    {

        // TODO:M.P add PLL support
        // pll = DriverInterface<DriverPLL>::Create(static_cast<PLLInstances >(BoardDefinitions
        // ::AUDIO_PLL),DriverPLLParams{});
        dmamux =
            DriverDMAMux::Create(static_cast<DMAMuxInstances>(BoardDefinitions::CELLULAR_DMAMUX), DriverDMAMuxParams{});
        dma = DriverDMA::Create(static_cast<DMAInstances>(BoardDefinitions::CELLULAR_DMA), DriverDMAParams{});


@@ 598,39 565,42 @@ namespace bsp

        switch (status) {
        case kStatus_LPUART_TxIdle: {
            logUARTdebug("[TX done]");
            logUARTdebug("[TX] Done");
            // task handle to be released in userData
            vTaskNotifyGiveFromISR((TaskHandle_t)userData, &higherPriorTaskWoken);

            portEND_SWITCHING_ISR(higherPriorTaskWoken);

            break;
        }
        case kStatus_LPUART_RxIdle:
            logUARTdebug("[RX] Chunk done. Flow control must hold the gate from now on");
            if (MoveRxDMAtoStreamBuf(RXdmaMaxReceivedCount) and StartReceive(GetFreeStreamBufferSize())) {
            RXfer.dataSize = RXdmaMaxReceivedCount;
            if (sendRxDmaResult(cellular::CellularResultCode::ReceivedAndFull) and
                startReceive(getMaxBufferDataSize())) {
                // usual mode: append a chunk and wait for line idle to finish receive
            }
            else {
                // the auxiliary exit path on stream buffer full
                RestartReceivingManually = true;
                FinishReceive();
            }
            RXdmaReceivedCount = -1;

            break;
        default:
            logUARTdebug("uartDMACallback status %ld", status);
            break;
        }
    }

    void RT1051Cellular::SetSendingAllowed(bool state)
    void RT1051Cellular::setSendingAllowed(bool state)
    {
        pv_SendingAllowed = state;
    }
    bool RT1051Cellular::GetSendingAllowed()
    bool RT1051Cellular::getSendingAllowed() const noexcept
    {
        return pv_SendingAllowed;
    }

    void RT1051Cellular::SelectAntenna(bsp::cellular::antenna antenna)
    void RT1051Cellular::selectAntenna(bsp::cellular::antenna antenna)
    {
        if (antenna == bsp::cellular::antenna::lowBand) {
            gpio_2->WritePin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_2_ANTSEL_PIN),


@@ 644,7 614,7 @@ namespace bsp
        }
    }

    bsp::cellular::antenna RT1051Cellular::GetAntenna()
    bsp::cellular::antenna RT1051Cellular::getAntenna()
    {
        // make sure ANTSEL pin has Software Input On Field set
        bool whichAntenna = gpio_2->ReadPin(magic_enum::enum_integer(BoardDefinitions::CELLULAR_GPIO_2_ANTSEL_PIN));


@@ 652,6 622,19 @@ namespace bsp
                                                                : bsp::cellular::antenna::highBand);
    }

    void RT1051Cellular::logData(const std::string &title, uint8_t *buffer, size_t nbytes)
    {
#if DEBUG_CELLULAR_UART == 1
        LOG_PRINTF("[%s: %d]", title.c_str(), nbytes);
        uint8_t *ptr = buffer;
        LOG_PRINTF("\n{");
        for (size_t i = 0; i < nbytes; i++) {
            LOG_PRINTF("%02X ", ptr[i]);
        }
        LOG_PRINTF("}\n");
#endif
    }

    namespace cellular
    {
        static xQueueHandle qhandle = nullptr;


@@ 716,7 699,7 @@ namespace bsp
            {
                BaseType_t xHigherPriorityTaskWoken = pdFALSE;
                if (qhandle != NULL) {
                    uint8_t val = static_cast<uint8_t>(IRQsource::statusPin);
                    std::uint8_t val = static_cast<std::uint8_t>(IRQsource::statusPin);
                    xQueueSendFromISR(qhandle, &val, &xHigherPriorityTaskWoken);
                }
                return xHigherPriorityTaskWoken;


@@ 727,12 710,12 @@ namespace bsp
        namespace sim
        {

            auto trayIRQ_handler() -> BaseType_t
            auto trayIRQHandler() -> BaseType_t
            {
                BaseType_t xHigherPriorityTaskWoken = pdFALSE;

                if (qhandle) {
                    uint8_t val = static_cast<uint8_t>(IRQsource::trayPin);
                    std::uint8_t val = static_cast<std::uint8_t>(IRQsource::trayPin);
                    xQueueSendFromISR(qhandle, &val, &xHigherPriorityTaskWoken);
                }
                return xHigherPriorityTaskWoken;


@@ 745,14 728,14 @@ namespace bsp
                return state;
            }

            void hotswap_trigger()
            void hotSwapTrigger()
            {
                GPIO_PinWrite(BSP_CELLULAR_SIM_CARD_PRESENCE_PORT, BSP_CELLULAR_SIM_CARD_PRESENCE_PIN, 1);
                vTaskDelay(100); // sleep for 100 ms...
                GPIO_PinWrite(BSP_CELLULAR_SIM_CARD_PRESENCE_PORT, BSP_CELLULAR_SIM_CARD_PRESENCE_PIN, 0);
            }

            void sim_sel()
            void simSelect()
            {
                if (Store::GSM::get()->selected == Store::GSM::SIM::SIM2) {
                    GPIO_PinWrite(BSP_CELLULAR_SIM_CARD_PRESENCE_PORT, BSP_CELLULAR_SIMSEL_PIN, 1);


@@ 765,16 748,15 @@ namespace bsp

        namespace ringIndicator
        {
            auto riIRQ_handler() -> BaseType_t
            auto riIRQHandler() -> BaseType_t
            {
                BaseType_t xHigherPriorityTaskWoken = pdFALSE;
                if (qhandle != NULL) {
                    uint8_t val = static_cast<uint8_t>(IRQsource::ringIndicatorPin);
                    std::uint8_t val = static_cast<std::uint8_t>(IRQsource::ringIndicatorPin);
                    xQueueSendFromISR(qhandle, &val, &xHigherPriorityTaskWoken);
                }
                return xHigherPriorityTaskWoken;
            }
        } // namespace ringIndicator

    } // namespace cellular
    }     // namespace cellular
} // namespace bsp

M module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.hpp => module-bsp/board/rt1051/bsp/cellular/rt1051_cellular.hpp +49 -83
@@ 1,18 1,18 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef PUREPHONE_RT1501_CELLULAR_HPP
#define PUREPHONE_RT1501_CELLULAR_HPP
#pragma once

#include "bsp/cellular/bsp_cellular.hpp"
#include <FreeRTOS.h>
#include <message_buffer.h>
#include <timers.h>

#include "FreeRTOS.h"
#include "stream_buffer.h"
#include "timers.h"
#include "board.h"
#include "fsl_lpuart.h"
#include "fsl_lpuart_edma.h"

#include "bsp/cellular/bsp_cellular.hpp"
#include "bsp/cellular/CellularResult.hpp"
#include "bsp/BoardDefinitions.hpp"

#include "drivers/pll/DriverPLL.hpp"


@@ 22,97 22,84 @@

namespace bsp
{

    class RT1051Cellular : public Cellular
    {
      private:
        bool pv_SendingAllowed = true;

        void logData(const std::string &title, uint8_t *buffer, size_t nbytes);

      public:
        RT1051Cellular();

        ~RT1051Cellular();

        void PowerUp() override final;

        void PowerDown() override final;

        void Restart() override final;

        uint32_t Wait(uint32_t timeout) override final;

        ssize_t Read(void *buf, size_t nbytes) override final;

        ssize_t Write(void *buf, size_t nbytes) override final;

        void InformModemHostAsleep() override final;

        void InformModemHostWakeup() override final;

        void EnterSleep() override final;

        void ExitSleep() override final;

        void SetSpeed(uint32_t portSpeed) override final;

        void SetSendingAllowed(bool state) override final;
        bool GetSendingAllowed() override final;

        static StreamBufferHandle_t uartRxStreamBuffer;
        static TimerHandle_t rxTimeoutTimer;

        void SelectAntenna(bsp::cellular::antenna antenna) override final;
        bsp::cellular::antenna GetAntenna() override final;

        void powerUp() override final;
        void powerDown() override final;
        void restart() override final;
        uint32_t wait(std::chrono::milliseconds timeoutMs) override final;
        ssize_t read(void *buffer, size_t nbytes, std::chrono::milliseconds timeoutMs) override final;
        ssize_t write(void *buf, size_t nbytes) override final;
        void informModemHostAsleep() override final;
        void informModemHostWakeup() override final;
        void enterSleep() override final;
        void exitSleep() override final;
        void setSpeed(uint32_t portSpeed) override final;
        void setSendingAllowed(bool state) override final;
        bool getSendingAllowed() const noexcept override final;
        void selectAntenna(bsp::cellular::antenna antenna) override final;
        bsp::cellular::antenna getAntenna() override final;

        static MessageBufferHandle_t uartRxBuffer;
        static lpuart_edma_handle_t uartDmaHandle;
        static bool RestartReceivingManually;
        static constexpr std::size_t RXdmaBufferSize = bsp::cellular::CellularResultStructMaxDataSize;
        static std::size_t RXdmaMaxReceivedCount;

        static void FinishReceive();
        static constexpr std::uint32_t edgeIRQMaskEnable  = kLPUART_RxActiveEdgeInterruptEnable;
        static constexpr std::uint32_t idleIRQMaskEnable  = kLPUART_IdleLineInterruptEnable;
        static constexpr std::uint32_t edgeIRQMask        = kLPUART_RxActiveEdgeFlag;
        static constexpr std::uint32_t idleIRQMask        = kLPUART_IdleLineFlag;
        static constexpr std::uint32_t dataRegFullIRQMask = kLPUART_RxDataRegFullFlag;

        static bool RestartReceivingManually;
        static bool sendRxDmaResult(bsp::cellular::CellularResultCode reason);
        static std::size_t getMaxBufferDataSize();
        static bool startReceive(std::size_t nbytes);

        static TaskHandle_t untilReceivedNewHandle;

      private:
        void MSPInit();

        void MSPDeinit();

        void DMAInit();

        void DMADeinit();

      public:
        static constexpr uint32_t startIRQMaskEnable =
            kLPUART_RxActiveEdgeInterruptEnable | kLPUART_RxDataRegFullInterruptEnable;
        static constexpr uint32_t finishIRQMaskEnable = kLPUART_IdleLineInterruptEnable;

        static constexpr uint32_t startIRQMask  = kLPUART_RxActiveEdgeFlag | kLPUART_RxDataRegFullFlag;
        static constexpr uint32_t finishIRQMask = kLPUART_IdleLineFlag;
        static void uartDMACallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData);

      private:
        static inline void EnableRx(uint32_t irqMask = startIRQMaskEnable | finishIRQMaskEnable)
        static inline void enableRx(uint32_t irqMask = edgeIRQMaskEnable | idleIRQMaskEnable | dataRegFullIRQMask)
        {
            LPUART_ClearStatusFlags(CELLULAR_UART_BASE, 0xFFFFFFFF);
            LPUART_EnableInterrupts(CELLULAR_UART_BASE, irqMask);
            LPUART_EnableRx(CELLULAR_UART_BASE, true);
        }

        static inline void DisableRx(uint32_t irqMask = startIRQMaskEnable | finishIRQMaskEnable)
        static inline void disableRx(uint32_t irqMask = edgeIRQMaskEnable | idleIRQMaskEnable | dataRegFullIRQMask)
        {
            LPUART_DisableInterrupts(CELLULAR_UART_BASE, irqMask);
            LPUART_ClearStatusFlags(CELLULAR_UART_BASE, irqMask);
            LPUART_EnableRx(CELLULAR_UART_BASE, false);
        }

        inline void EnableTx()
        inline void enableTx()
        {
            LPUART_EnableTx(CELLULAR_UART_BASE, true);
        }

        inline void DisableTx()
        inline void disableTx()
        {
            LPUART_EnableTx(CELLULAR_UART_BASE, false);
        }

        // M.P: It is important to destroy these drivers in specific order
        std::shared_ptr<drivers::DriverPLL> pll;
        std::shared_ptr<drivers::DriverGPIO> gpio_1;
        std::shared_ptr<drivers::DriverGPIO> gpio_2;


@@ 122,32 109,11 @@ namespace bsp
        std::unique_ptr<drivers::DriverDMAHandle> txDMAHandle;
        std::unique_ptr<drivers::DriverDMAHandle> rxDMAHandle;

        static void uartDMACallback(LPUART_Type *base, lpuart_edma_handle_t *handle, status_t status, void *userData);

      public:
        static TaskHandle_t blockedTaskHandle;

      private:
        // Constants
        const static uint32_t baudrate                               = 115200;
        const static uint32_t rxStreamBufferLength                   = 1024;
        const static uint32_t rxStreamBufferNotifyWatermark          = 1;
        const static uint32_t CELLULAR_BSP_AP_READY_PIN_ACTIVE_STATE = 1;
        const static uint32_t CELLULAR_BSP_ANTSEL_PIN_A_STATE        = 0;
        const static uint32_t CELLULAR_BSP_ANTSEL_PIN_B_STATE        = 1;

      public:
        static constexpr size_t RXdmaBufferSize = 16;
        static uint8_t RXdmaBuffer[RXdmaBufferSize];
        static ssize_t RXdmaReceivedCount;
        static size_t RXdmaMaxReceivedCount;
        static bool MoveRxDMAtoStreamBuf(size_t nbytes);
        static size_t GetFreeStreamBufferSize();
        static bool StartReceive(size_t nbytes);

        static TaskHandle_t untilReceivedNewHandle;
        static constexpr std::uint32_t baudrate                               = 115200;
        static constexpr std::uint32_t rxMessageBufferLength                  = 1024;
        static constexpr std::uint16_t messageBufferOverheadSize              = 4;
        static constexpr std::uint32_t CELLULAR_BSP_AP_READY_PIN_ACTIVE_STATE = 1;
        static constexpr std::uint32_t CELLULAR_BSP_ANTSEL_PIN_A_STATE        = 0;
        static constexpr std::uint32_t CELLULAR_BSP_ANTSEL_PIN_B_STATE        = 1;
    };

} // namespace bsp

#endif // PUREPHONE_RT1501_CELLULAR_HPP

M module-bsp/board/rt1051/common/irq/irq_gpio.cpp => module-bsp/board/rt1051/common/irq/irq_gpio.cpp +2 -2
@@ 122,7 122,7 @@ namespace bsp
            }

            if (irq_mask & (1 << BSP_CELLULAR_SIM_TRAY_INSERTED_PIN)) {
                xHigherPriorityTaskWoken |= bsp::cellular::sim::trayIRQ_handler();
                xHigherPriorityTaskWoken |= bsp::cellular::sim::trayIRQHandler();
            }

            if (irq_mask & (1 << static_cast<uint32_t>(BoardDefinitions::LIGHT_SENSOR_IRQ))) {


@@ 154,7 154,7 @@ namespace bsp
            }

            if (irq_mask & (1 << BSP_CELLULAR_RI_PIN)) {
                bsp::cellular::ringIndicator::riIRQ_handler();
                bsp::cellular::ringIndicator::riIRQHandler();
            }

            // Clear all IRQs

A module-bsp/bsp/cellular/CellularResult.hpp => module-bsp/bsp/cellular/CellularResult.hpp +157 -0
@@ 0,0 1,157 @@
#pragma once

#include <memory>
#include <module-utils/Utils.hpp>
#include <utility>

namespace bsp::cellular
{
    enum class CellularResultCode : std::uint8_t
    {
        Uninitialized,
        ReceivedAndIdle,
        ReceivedAndFull,
        ReceivedAfterFull,
        ReceivingNotStarted,
        ReceivedNoData,
        TransmittingNotStarted,
        CMUXFrameError,
    };

    constexpr size_t CellularResultStructMaxDataSize = 128;
    constexpr size_t CellularResultStructEmptySize   = 5;

    struct __attribute__((__packed__)) CellularDMAResultStruct
    {
        CellularResultCode resultCode = CellularResultCode::ReceivedNoData;
        size_t dataSize               = 0;
        std::uint8_t data[CellularResultStructMaxDataSize];

        constexpr static size_t getEmptySize()
        {
            return sizeof(CellularDMAResultStruct) - CellularResultStructMaxDataSize;
        }

        constexpr static size_t getMaxSize()
        {
            return getEmptySize() + sizeof(data);
        }

        size_t getSize() const noexcept
        {
            return getEmptySize() + dataSize;
        }
    };

    struct CellularResultStruct
    {
        CellularResultCode resultCode = CellularResultCode::Uninitialized;
        std::vector<uint8_t> data;

        [[nodiscard]] auto serialize() const -> std::unique_ptr<uint8_t>
        {
            auto serialized     = std::unique_ptr<uint8_t>(new uint8_t[data.size() + sizeof(resultCode)]);
            serialized.get()[0] = static_cast<unsigned char>(resultCode);

            if (data.size() > 0) {
                memcpy(serialized.get() + 1, data.data(), data.size());
            }

            return serialized;
        }

        auto deserialize(const uint8_t *serialized, const size_t size) -> void
        {
            if (size > 0) {
                resultCode = static_cast<CellularResultCode>(*serialized);
                if (size > 1) {
                    data = {serialized + 1, serialized + size};
                }
            }
        }

        [[nodiscard]] auto getSerializedSize() const noexcept -> size_t
        {
            return (sizeof(resultCode) + data.size());
        }
    };

    class CellularResult
    {
        CellularResultStruct result;

      public:
        CellularResult() : result{CellularResultCode::Uninitialized, {}}
        {}

        explicit CellularResult(CellularDMAResultStruct &result)
            : result{result.resultCode, {result.data, result.data + result.dataSize}}
        {}

        explicit CellularResult(CellularResultStruct result) : result{std::move(result)}
        {}

        explicit CellularResult(const uint8_t *serialized, const size_t size)
        {
            result.deserialize(serialized, size);
        }

        auto getResultCode() const noexcept -> CellularResultCode
        {
            return result.resultCode;
        }

        auto setResultCode(CellularResultCode code) -> void
        {
            result.resultCode = code;
        }

        auto getData() const noexcept -> std::vector<uint8_t> const &
        {
            return result.data;
        }

        auto setData(const std::vector<uint8_t> &data) -> void
        {
            result.data = data;
        }

        auto getDataAsString() const -> std::string
        {
            return std::string{result.data.begin(), result.data.end()};
        }

        auto getStruct() const -> CellularResultStruct const *
        {
            return &result;
        }

        auto getSerialized() const -> std::unique_ptr<uint8_t>
        {
            return result.serialize();
        }

        auto getSerializedSize() const noexcept -> size_t
        {
            return result.getSerializedSize();
        }
    };
} // namespace bsp::cellular

inline const char *c_str(bsp::cellular::CellularResultCode result)
{
    switch (result) {
    case bsp::cellular::CellularResultCode::ReceivedAndIdle:
        return "ReceivedAndIdle";
    case bsp::cellular::CellularResultCode::ReceivedAndFull:
        return "ReceivedAndFull";
    case bsp::cellular::CellularResultCode::ReceivedAfterFull:
        return "ReceivedAfterFull";
    case bsp::cellular::CellularResultCode::ReceivingNotStarted:
        return "ReceivingNotStarted";
    case bsp::cellular::CellularResultCode::TransmittingNotStarted:
        return "TransmittingNotStarted";
    default:
        return "Unknown";
    }
}

M module-bsp/bsp/cellular/bsp_cellular.cpp => module-bsp/bsp/cellular/bsp_cellular.cpp +11 -12
@@ 8,11 8,10 @@
#error "Unsupported target"
#endif


namespace bsp{

    std::optional<std::unique_ptr<Cellular>> Cellular::Create(
            [[maybe_unused]] const char* term, uint32_t portSpeed) {
namespace bsp
{
    std::optional<std::unique_ptr<Cellular>> Cellular::create([[maybe_unused]] const char *term, uint32_t portSpeed)
    {

        std::unique_ptr<Cellular> inst;



@@ 24,26 23,26 @@ namespace bsp{
#error "Unsupported target"
#endif

        if(inst->isInitialized){
        if (inst->isInitialized) {
            return inst;
        }

        return {};
    }

    [[nodiscard]] auto Cellular::GetCellularDevice() const noexcept -> std::shared_ptr<devices::Device>
    [[nodiscard]] auto Cellular::getCellularDevice() const noexcept -> std::shared_ptr<devices::Device>
    {
        return driverLPUART;
    }

    [[nodiscard]] auto Cellular::GetLastCommunicationTimestamp() const noexcept -> TickType_t
    [[nodiscard]] auto Cellular::getLastCommunicationTimestamp() const noexcept -> TickType_t
    {
    	return lastCommunicationTimestamp;
        return lastCommunicationTimestamp;
    }

    [[nodiscard]] auto Cellular::IsCellularInSleepMode() const noexcept -> bool
    [[nodiscard]] auto Cellular::isCellularInSleepMode() const noexcept -> bool
    {
    	return isInSleepMode;
        return isInSleepMode;
    }

}
} // namespace bsp

M module-bsp/bsp/cellular/bsp_cellular.hpp => module-bsp/bsp/cellular/bsp_cellular.hpp +61 -51
@@ 5,61 5,65 @@
#include <stdint.h>
#include <stddef.h>
#include <memory>
#include <chrono>
#include <FreeRTOS.h>
#include <FreeRTOS/include/queue.h>
#include "drivers/lpuart/DriverLPUART.hpp"
#include <common_data/EventStore.hpp>

namespace bsp {
namespace cellular
namespace bsp
{
    namespace cellular
    {
    	enum class antenna{
    		lowBand,
			highBand
    	};
        enum class antenna
        {
            lowBand,
            highBand
        };
    }
    class Cellular {
    public:

        static std::optional<std::unique_ptr<Cellular>> Create(const char* term = "/dev/ttyUSB0", uint32_t portSpeed = 115200);
    class Cellular
    {
      public:
        static std::optional<std::unique_ptr<Cellular>> create(const char *term   = "/dev/ttyUSB0",
                                                               uint32_t portSpeed = 115200);

        Cellular() {}
        virtual ~Cellular() {}
        Cellular()          = default;
        virtual ~Cellular() = default;

        virtual void powerUp() = 0;

        virtual void PowerUp() = 0;
        virtual void powerDown() = 0;

        virtual void PowerDown() = 0;
        virtual void restart() = 0;

        virtual void Restart() = 0;
        virtual uint32_t wait(std::chrono::milliseconds timeoutMs) = 0;

        virtual uint32_t Wait(uint32_t timeout) = 0;
        virtual ssize_t read(void *buf, size_t nbytes, std::chrono::milliseconds timeoutMs) = 0;

        virtual ssize_t Read(void *buf, size_t nbytes) = 0;
        virtual ssize_t write(void *buf, size_t nbytes) = 0;

        virtual ssize_t Write(void *buf, size_t nbytes) = 0;
        virtual void informModemHostAsleep() = 0;

        virtual void InformModemHostAsleep() = 0;
        virtual void informModemHostWakeup() = 0;

		virtual void InformModemHostWakeup() = 0;
        virtual void enterSleep() = 0;

        virtual void EnterSleep() = 0;
        virtual void exitSleep() = 0;

        virtual void ExitSleep() = 0;
        virtual void setSpeed(uint32_t portSpeed) = 0;

        virtual void SetSpeed(uint32_t portSpeed) = 0;
        virtual void setSendingAllowed(bool state) = 0;

        virtual void SetSendingAllowed(bool state) = 0;
        virtual bool GetSendingAllowed()           = 0;
        virtual bool getSendingAllowed() const noexcept = 0;

        virtual void SelectAntenna(bsp::cellular::antenna antenna) = 0;
        virtual bsp::cellular::antenna GetAntenna() = 0;
        virtual void selectAntenna(bsp::cellular::antenna antenna) = 0;
        virtual bsp::cellular::antenna getAntenna()                = 0;

        [[nodiscard]] auto GetCellularDevice() const noexcept -> std::shared_ptr<devices::Device>;
        [[nodiscard]] auto GetLastCommunicationTimestamp() const noexcept -> TickType_t;
        [[nodiscard]] auto IsCellularInSleepMode() const noexcept -> bool;
        [[nodiscard]] auto getCellularDevice() const noexcept -> std::shared_ptr<devices::Device>;
        [[nodiscard]] auto getLastCommunicationTimestamp() const noexcept -> TickType_t;
        [[nodiscard]] auto isCellularInSleepMode() const noexcept -> bool;

    protected:
      protected:
        bool isInitialized = false;
        bool isInSleepMode{false};
        TickType_t lastCommunicationTimestamp;


@@ 67,36 71,42 @@ namespace cellular
        std::shared_ptr<drivers::DriverLPUART> driverLPUART;
    };
    namespace cellular
    {
        enum IRQsource
        {
        enum IRQsource{
            statusPin,
            trayPin,
			ringIndicatorPin,
            ringIndicatorPin,
        };

        /// initialize SIM queue directed to EventWorker
        int init(xQueueHandle qHandle);

        namespace USB {
            enum class PassthroughState {
        namespace USB
        {
            enum class PassthroughState
            {
                ENABLED,
                DISABLED,
            };

            enum class BootPinState {
            enum class BootPinState
            {
                FIRMWARE_UPGRADE,
                NORMAL_BOOT,
            };

            BootPinState getBootPin() ;
            PassthroughState getPassthrough() ;
            BootPinState getBootPin();
            PassthroughState getPassthrough();

            void setBootPin(BootPinState bootPin);
            void setPassthrough(PassthroughState pass);
        }
        } // namespace USB

        namespace status{
            enum class value {
        namespace status
        {
            enum class value
            {
                /// from the docs: When the module is turned on normally, the STATUS will present the low state.
                ACTIVE,
                INACTIVE,


@@ 104,27 114,27 @@ namespace cellular
            /// returns true if status OK. Open drain pulled up; ground is OK.
            bsp::cellular::status::value getStatus();
            BaseType_t statusIRQhandler();
        }
        } // namespace status

        namespace sim
        {
            /// handler for SIM tray which is connected to phone, not GSM
            BaseType_t trayIRQ_handler();
            BaseType_t trayIRQHandler();

            Store::GSM::Tray getTray();
            /// trigger swap pin on gsm so that it would reload sim card in tray
            /// after that +QPIN urc should come
            void hotswap_trigger();
            void sim_sel();
            void hotSwapTrigger();
            void simSelect();
        } // namespace sim

        namespace ringIndicator
		{
        	// handling incoming calls and sms - RI pin
        	BaseType_t riIRQ_handler();
		} // namespace RingIndicator
        {
            // handling incoming calls and sms - RI pin
            BaseType_t riIRQHandler();
        } // namespace ringIndicator

    }     // namespace cellular
};        // namespace bsp
    } // namespace cellular
};    // namespace bsp

#endif // PUREPHONE_BSP_CELLULAR_HPP

M module-cellular/Modem/ATCommon.cpp => module-cellular/Modem/ATCommon.cpp +44 -22
@@ 8,7 8,6 @@
#include <log/log.hpp>
#include <string>
#include <ticks.hpp>
#include <vector>
#include <inttypes.h> // for PRIu32
#include <Utils.hpp>
#include "ATStream.hpp"


@@ 27,22 26,21 @@ const std::string Channel::CMS_ERROR  = "+CMS ERROR:";
// const std::string Channel::RING = "RING";
// const std::string Channel::NO_DIALTONE = "NO DIALTONE";

void Channel::cmd_log(std::string cmd, const Result &result, uint32_t timeout)
void Channel::cmdLog(std::string cmd, const Result &result, std::chrono::milliseconds timeout)
{
    cmd.erase(std::remove(cmd.begin(), cmd.end(), '\r'), cmd.end());
    cmd.erase(std::remove(cmd.begin(), cmd.end(), '\n'), cmd.end());
    switch (result.code) {
    case Result::Code::TIMEOUT: {
        LOG_INFO("[AT]: >%s<, timeout %" PRIu32
                 " - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf",
                 cmd.c_str(),
                 timeout);
        LOG_ERROR("[AT]: >%s<, timeout %s - please check the value with Quectel_EC25&EC21_AT_Commands_Manual_V1.3.pdf",
                  cmd.c_str(),
                  utils::to_string(timeout.count()).c_str());
    } break;
    case Result::Code::ERROR: {
        LOG_INFO("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
        LOG_ERROR("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
    } break;
    default:
        LOG_INFO("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
        LOG_DEBUG("[AT]: >%s<, >%s<", cmd.c_str(), result.response.size() ? result.response.back().c_str() : "");
        break;
    }
#if DEBUG_MODEM_OUTPUT_RESPONSE


@@ 64,30 62,31 @@ std::string Channel::formatCommand(const std::string &cmd) const
Result Channel::cmd(const std::string &cmd, std::chrono::milliseconds timeout, size_t rxCount)
{
    Result result;
    blockedTaskHandle = xTaskGetCurrentTaskHandle();
    ATStream atStream(rxCount);

    awaitingResponseFlag.set();

    cmd_init();
    std::string cmdFixed = formatCommand(cmd);

    cmd_send(cmdFixed);

    uint32_t currentTime   = cpp_freertos::Ticks::GetTicks();
    uint32_t timeoutNeeded = ((timeout.count() == UINT32_MAX) ? UINT32_MAX : currentTime + timeout.count());
    uint32_t timeElapsed   = currentTime;

    ATStream atStream(rxCount);
    auto startTime = std::chrono::steady_clock::now();
    auto endTime   = startTime + timeout;

    while (true) {
        if (timeoutNeeded != UINT32_MAX && timeElapsed >= timeoutNeeded) {
        if (std::chrono::steady_clock::now() > endTime) {
            result.code = Result::Code::TIMEOUT;
            break;
        }

        auto taskUnlocked = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed);
        timeElapsed       = cpp_freertos::Ticks::GetTicks();
        if (size_t bytesRead = cmd_receive(receiveBuffer.get(), std::chrono::milliseconds{0}); bytesRead > 0) {
            auto cellularResult = bsp::cellular::CellularResult{receiveBuffer.get(), bytesRead};

        if (taskUnlocked) {
            atStream.write(cmd_receive());
            if (result = checkResult(cellularResult.getResultCode()); result.code != at::Result::Code::OK) {
                break;
            }

            atStream.write(cellularResult.getDataAsString());

            if (atStream.isReady()) {
                result = atStream.getResult();


@@ 96,9 95,10 @@ Result Channel::cmd(const std::string &cmd, std::chrono::milliseconds timeout, s
        }
    }

    blockedTaskHandle = nullptr;
    awaitingResponseFlag.clear();

    cmd_post();
    cmd_log(cmdFixed, result, timeout.count());
    cmdLog(cmdFixed, result, timeout);

    return result;
}


@@ 115,3 115,25 @@ auto Channel::cmd(const at::AT &at) -> Result
    auto time = utils::time::Scoped("Time to run at command" + cmd.getCmd());
    return this->cmd(cmd);
}

Result Channel::checkResult(bsp::cellular::CellularResultCode cellularResult)
{
    Result result;

    switch (cellularResult) {
    case bsp::cellular::CellularResultCode::ReceivingNotStarted:
        result.code = Result::Code::RECEIVING_NOT_STARTED;
        break;
    case bsp::cellular::CellularResultCode::TransmittingNotStarted:
        result.code = Result::Code::TRANSMISSION_NOT_STARTED;
        break;
    case bsp::cellular::CellularResultCode::CMUXFrameError:
        result.code = Result::Code::CMUX_FRAME_ERROR;
        break;
    default:
        result.code = Result::Code::OK;
        break;
    }

    return result;
}

M module-cellular/Modem/ATCommon.hpp => module-cellular/Modem/ATCommon.hpp +44 -9
@@ 7,6 7,7 @@
#include <at/ErrorCode.hpp>
#include <mutex.hpp>
#include <task.h>
#include <message_buffer.h>
#include "BaseChannel.hpp"

namespace sys


@@ 16,7 17,37 @@ namespace sys

namespace at
{
    constexpr auto delimiter = "\r\n"; /// use std::strlen()
    constexpr auto delimiter                         = "\r\n"; /// use std::strlen()
    constexpr auto defaultBufferTimeoutMs            = std::chrono::milliseconds{50};
    inline constexpr size_t defaultReceiveBufferSize = 128; // receiveBuffer size used for single message processing
    inline constexpr size_t defaultMessageBufferSize =
        defaultReceiveBufferSize * 2; // MessageBuffer size for passing data between tasks

    struct AwaitingResponseFlag
    {
      private:
        cpp_freertos::MutexStandard mutex;
        bool isWaiting = false;

      public:
        void set()
        {
            cpp_freertos::LockGuard lock{mutex};
            isWaiting = true;
        }

        void clear()
        {
            cpp_freertos::LockGuard lock{mutex};
            isWaiting = false;
        }

        bool state()
        {
            cpp_freertos::LockGuard lock{mutex};
            return isWaiting;
        }
    };

    class Channel : public BaseChannel
    {


@@ 25,8 56,10 @@ namespace at
        const std::array<char, 3> validTerm = {cmdSeparator, ',', '='};
        [[nodiscard]] auto formatCommand(const std::string &cmd) const -> std::string;

        MessageBufferHandle_t responseBuffer = nullptr;
        std::unique_ptr<uint8_t> receiveBuffer;
        AwaitingResponseFlag awaitingResponseFlag;
        cpp_freertos::MutexStandard mutex;
        TaskHandle_t blockedTaskHandle = nullptr;

      public:
        static const std::string OK;


@@ 41,17 74,19 @@ namespace at
        // const std::string Channel::RING = "RING";
        // const std::string Channel::NO_DIALTONE = "NO DIALTONE";

        explicit Channel(uint8_t *receiveBuffer)
            : receiveBuffer{receiveBuffer} {

              };

        /// waits till ok or timeout
        virtual auto cmd(const std::string &cmd,
                         std::chrono::milliseconds timeout = at::default_timeout,
                         size_t rxCount                    = 0) -> Result final;
        virtual auto cmd(const at::AT &at) -> Result final;
        virtual auto cmd(const at::Cmd &at) -> Result final;
        virtual void cmd_log(std::string cmd, const Result &result, uint32_t timeout) final;
        virtual auto ProcessNewData(sys::Service *service) -> int
        {
            return 0;
        }
    };
        virtual void cmdLog(std::string cmd, const Result &result, std::chrono::milliseconds timeout) final;

}; // namespace at
        Result checkResult(bsp::cellular::CellularResultCode cellularResult);
    };
} // namespace at

M module-cellular/Modem/ATParser.cpp => module-cellular/Modem/ATParser.cpp +46 -38
@@ 10,27 10,27 @@
#include <utility>
#include <vector>

ATParser::ATParser(bsp::Cellular *cellular) : cellular(cellular)
ATParser::ATParser(bsp::Cellular *cellular) : Channel{new uint8_t[at::defaultReceiveBufferSize]}, cellular(cellular)
{
    isInitialized = true;
    responseBuffer = xMessageBufferCreate(at::defaultMessageBufferSize);
}

/// plz see 12.7 summary of urc in documentation
std::vector<ATParser::Urc> ATParser::ParseURC()
{

    std::vector<ATParser::Urc> resp;
    size_t maxPos = 0, pos = 0;

    cpp_freertos::LockGuard lock(mutex);

    std::vector<std::pair<std::string, ATParser::Urc>> vals = {
        {"RDY", ATParser::Urc::MeInitializationSuccessful},
        {"+CFUN: 1", ATParser::Urc::FullFuncionalityAvailable},
    };

    std::vector<ATParser::Urc> resp;
    resp.reserve(vals.size() + 1);
    cpp_freertos::LockGuard lock(mutex);

    for (const auto &el : vals) {
        pos = responseBuffer.find(el.first);
        pos = urcBuffer.find(el.first);
        if (pos != std::string::npos) {
            resp.push_back(el.second);
            maxPos = std::max(pos + el.first.length(), maxPos);


@@ 38,50 38,54 @@ std::vector<ATParser::Urc> ATParser::ParseURC()
        }
    }

    if (responseBuffer.find("+QIND: \"FOTA\"") != std::string::npos) {
        LOG_DEBUG("%s", responseBuffer.c_str());
    if (urcBuffer.find("+QIND: \"FOTA\"") != std::string::npos) {
        LOG_DEBUG("%s", urcBuffer.c_str());
        resp.push_back(ATParser::Urc::Fota);
        return resp;
    }

    // manage string buffer
    if (maxPos == 0) {}
    else if (responseBuffer.size() >= maxPos) {
        responseBuffer.erase();
    else if (urcBuffer.size() >= maxPos) {
        urcBuffer.erase();
    }
    else {
        responseBuffer = responseBuffer.substr(maxPos);
        urcBuffer = urcBuffer.substr(maxPos);
    }

    return resp;
}

int ATParser::ProcessNewData(sys::Service *service)
at::Result ATParser::ProcessNewData(sys::Service *service, bsp::cellular::CellularResult &cellularResult)
{
    char rawBuffer[256] = {0};

    LOG_DEBUG("Receiving data from ProcessNewData");
    auto length = cellular->Read(rawBuffer, sizeof(rawBuffer));
    at::Result result;

    {
        cpp_freertos::LockGuard lock(mutex);
        responseBuffer.append(reinterpret_cast<char *>(rawBuffer), length);
        LOG_DEBUG("Appending %d bytes to responseBuffer[%u]: %s",
                  static_cast<int>(length),
                  static_cast<unsigned int>(responseBuffer.size()),
                  utils::removeNewLines(responseBuffer).c_str());
        urcBuffer.append(cellularResult.getDataAsString());
    }

    auto ret = ParseURC();
    if (blockedTaskHandle) {
        xTaskNotifyGive(blockedTaskHandle);

    if (awaitingResponseFlag.state()) {
        if (!xMessageBufferSend(responseBuffer,
                                cellularResult.getSerialized().get(),
                                cellularResult.getSerializedSize(),
                                pdMS_TO_TICKS(at::defaultBufferTimeoutMs.count()))) {
            LOG_DEBUG("[AT] Message buffer full!");
            result.code = at::Result::Code::FULL_MSG_BUFFER;
        }
    }
    else if (ret.size()) {
    else if (!ret.empty()) {
        if (ret.size() == 1 && ret[0] == ATParser::Urc::Fota) {
            std::string fotaData(responseBuffer);
            std::string fotaData;
            {
                cpp_freertos::LockGuard lock(mutex);
                fotaData = std::string(urcBuffer);
                urcBuffer.erase();
            }
            LOG_DEBUG("parsing FOTA:\"%s\"", fotaData.c_str());
            FotaService::API::sendRawProgress(service, fotaData);
            responseBuffer.erase();
        }
        else {
            urcs.insert(std::end(urcs), std::begin(ret), std::end(ret));


@@ 90,36 94,40 @@ int ATParser::ProcessNewData(sys::Service *service)
        // 1) RDY
        // 2) +CFUN: 1
        if (urcs.size() == 2) {
            cpp_freertos::LockGuard lock(mutex);
            auto msg = std::make_shared<CellularPowerUpProcedureCompleteNotification>();
            service->bus.sendMulticast(msg, sys::BusChannel::ServiceCellularNotifications);
            LOG_DEBUG("[!!!] Fucking away data");
            responseBuffer.erase();
            service->bus.sendMulticast(std::move(msg), sys::BusChannel::ServiceCellularNotifications);
            urcs.clear();

            cpp_freertos::LockGuard lock(mutex);
            urcBuffer.erase();
        }
    }
    return 1;
    else {
        result.code = at::Result::Code::DATA_NOT_USED;
    }

    return result;
}

void ATParser::cmd_init()
{
    cpp_freertos::LockGuard lock(mutex);
    responseBuffer.erase();
    urcBuffer.erase();
}

void ATParser::cmd_send(std::string cmd)
{
    cellular->Write(const_cast<char *>(cmd.c_str()), cmd.size());
    cellular->write(const_cast<char *>(cmd.c_str()), cmd.size());
}

std::string ATParser::cmd_receive()
size_t ATParser::cmd_receive(std::uint8_t *buffer, std::chrono::milliseconds timeout = std::chrono::milliseconds{0})
{
    cpp_freertos::LockGuard lock(mutex);
    return responseBuffer;
    return xMessageBufferReceive(responseBuffer, buffer, 256, pdMS_TO_TICKS(timeout.count()));
}

void ATParser::cmd_post()
{
    cpp_freertos::LockGuard lock(mutex);
    responseBuffer.erase(); // TODO:M.P is it okay to flush buffer here ?
    urcBuffer.erase();
    xMessageBufferReset(responseBuffer);
}

M module-cellular/Modem/ATParser.hpp => module-cellular/Modem/ATParser.hpp +9 -6
@@ 11,6 11,9 @@
#include <optional>
#include <task.h>
#include <vector>
#include <message_buffer.h>

#include <bsp/cellular/CellularResult.hpp>

namespace bsp
{


@@ 34,19 37,19 @@ class ATParser : public at::Channel
    ATParser(bsp::Cellular *cellular);
    virtual ~ATParser() = default;

    int ProcessNewData(sys::Service *service);
    at::Result ProcessNewData(sys::Service *service, bsp::cellular::CellularResult &cellularResult);

    virtual void cmd_init() override final;
    virtual void cmd_send(std::string cmd) override final;
    virtual std::string cmd_receive() override final;
    virtual size_t cmd_receive(std::uint8_t *buffer, std::chrono::milliseconds timeout) override final;
    virtual void cmd_post() override final;

  private:
    std::vector<Urc> ParseURC();
    bsp::Cellular *cellular = nullptr;
    std::string responseBuffer;
    std::vector<ATParser::Urc> urcs;
    bool isInitialized = false;
    bsp::Cellular *cellular              = nullptr;
    MessageBufferHandle_t responseBuffer = nullptr;
    std::string urcBuffer                = {};
    std::vector<ATParser::Urc> urcs      = {};
};

#endif // PUREPHONE_ATPARSER_HPP

M module-cellular/Modem/BaseChannel.hpp => module-cellular/Modem/BaseChannel.hpp +6 -6
@@ 7,7 7,7 @@
#include <vector>
#include <at/Commands.hpp>
#include <at/Result.hpp>
#include <at/Constants.hpp>
#include <bsp/cellular/CellularResult.hpp>

namespace at
{


@@ 29,12 29,12 @@ namespace at

        /// @defgroup Channel Platform independent methods
        /// {
        virtual void cmd_log(std::string cmd, const Result &result, uint32_t timeout)
        virtual void cmdLog(std::string cmd, const Result &result, std::chrono::milliseconds timeout)
        {}
        virtual void cmd_init()                = 0;
        virtual void cmd_send(std::string cmd) = 0;
        virtual std::string cmd_receive()      = 0;
        virtual void cmd_post()                = 0;
        virtual void cmd_init()                                                               = 0;
        virtual void cmd_send(std::string cmd)                                                = 0;
        virtual size_t cmd_receive(std::uint8_t *buffer, std::chrono::milliseconds timeoutMs) = 0;
        virtual void cmd_post()                                                               = 0;
        /// }
    };
} // namespace at

A module-cellular/Modem/README.md => module-cellular/Modem/README.md +98 -0
@@ 0,0 1,98 @@
![alt text](./doc/Images/mudita_logo.png "MUDITA")

# Cellular Multiplexer

## Table of contents
1. [History](#history)
2. [Modes](#modes)
3. [Single command data flow](#singlecmd)
5. [Cellular result structures](#result)
6. [Error codes](#errors)

## History <a name="history"></a>
| Authors           | Change description        | Status | Modification date |
| ----------------- | ------------------------- | ------ | ----------------- |
| Maciej Janicki    | Initial version           | Draft  | 2021.03.24        |

## Modes <a name="modes"></a>
Cellular operates in three modes:
 - AT,
 - CMUX setup,
 - CMUX

The AT mode is used for communication setup and modem configuration.

![alt text](./doc/Images/at_mode.svg "Flow diagram for at mode")

The CMUX setup is an intermidiate state starting a CMUX communication:
 - It configures CMUX mode's transmission parameters.
 - It creates channels for a frame multiplexing (Commands, Notifications, Data).
Data flow for both CMUX and CMUX setup in current implementation is identical.

![alt text](./doc/Images/mux_mode.svg "Flow diagram for mux mode")

## Single command data flow <a name="singlecmd"></a>
![alt text](./doc/Images/single_cmd_transmission.png "Single command sequence")

A single command sequence starts with a call from the Service and can end in one of three ways:
 - Command response timeout (timeout depends on command issued).
 - Service receives an error in result and handles it accordingly. 
 - Command response is successfully parsed before timeout and passed to service layer.

## Result structs <a name="result"></a>

Currently, there are two types of structs used to pass data between contexts.

DMA result struct is used to directly store data from DMA engine as well as pass these through 
MessageBuffer to Worker. This struct has static data buffer maximum size.

![alt text](./doc/Images/dma_result_struct.png "Dma result struct")

Cellular Result is used to pass complete frames serialized in vector together with 
result code. This struct has variable size.

![alt text](./doc/Images/cellular_result_struct.png "Cellular result struct")

## Error codes <a name="errors"></a>


![alt text](./Images/class_channel.png "Class diagram for channel")

Currently there are different errors/results codes for different layers.

  1. CellularResultCode
     This code is used to pass information from bsp layer up to channel.
     It can be either used to handle flow control messages from DMA or 
     to pass failures to upper layer (transmission/receiving not starter, 
     cmux frame error).

     ```
     Uninitialized,
     ReceivedAndIdle,
     ReceivedAndFull,
     ReceivedAfterFull,
     ReceivedNoData,
     ReceivingNotStarted,
     TransmittingNotStarted,
     CMUXFrameError, 
     ```

  2. AtResult
     This result code is passed to service layer. 

        ```
        OK,              /// at OK
        ERROR,           /// at ERROR For compatibility also for CME_ERROR and CMS_ERROR (details in errorCode)
        CME_ERROR,       /// In case CME error see errorCode
        CMS_ERROR,       /// In case CMS error see errorCode
        TIMEOUT,         /// at Timeout
        TOKENS,          /// at numbers of tokens needed met
        NONE,            /// no code
        UNDEFINED,       /// undefined result - usage of Undefined result, define and pin result to use it
        PARSING_ERROR,   /// parser error
        FULL_MSG_BUFFER, /// at not enough space left in message buffer for new message
        TRANSMISSION_NOT_STARTED, /// at dma not starting transmission
        RECEIVING_NOT_STARTED,    /// at dma not starting requested receiving
        DATA_NOT_USED,            /// at received data not being used
        CMUX_FRAME_ERROR,         /// at cmux deserialize error
        ```

M module-cellular/Modem/TS0710/DLC_channel.cpp => module-cellular/Modem/TS0710/DLC_channel.cpp +113 -125
@@ 1,50 1,44 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

/**
 * Project Untitled
 */

#include "DLC_channel.h"
#include "TS0710.h"

#include "TS0710_DATA.h"
#include "TS0710_DLC_ESTABL.h"
#include "TS0710_DLC_RELEASE.h"
#include "TS0710_Frame.h"
#include "log/log.hpp"
#include "ticks.hpp"

#include <module-utils/log/log.hpp>
#include <ticks.hpp>
#include <Utils.hpp>
#include <cstdlib>

/**
 * DLC_channel implementation
 */
#include <magic_enum.hpp>

DLC_channel::DLC_channel(DLCI_t DLCI, std::string name, bsp::Cellular *cellular, Callback_t callback)
DLC_channel::DLC_channel(DLCI_t DLCI, const std::string &name, bsp::Cellular *cellular, const Callback_t &callback)
    : Channel{new uint8_t[at::defaultReceiveBufferSize]}, pv_name{name}, pv_DLCI{DLCI}, pv_cellular{cellular}
{
    LOG_DEBUG("Creating DLCI %i channel \"%s\"", DLCI, name.c_str());
    pv_name     = name;
    pv_DLCI     = DLCI;
    pv_cellular = cellular;

    if (callback != nullptr)
    if (callback != nullptr) {
        pv_callback = callback;
    }

    chanParams.TypeOfFrame             = TypeOfFrame_e::SABM;
    chanParams.ConvergenceLayer        = 1;
    chanParams.Priority                = 1;
    chanParams.AckTime                 = 100; // 100ms default
    chanParams.MaxFrameSize            = 128;
    chanParams.MaxNumOfRetransmissions = 3; // default 3
    chanParams.ErrRecovWindowSize      = 2; // default 2

    DLC_ESTABL_SystemParameters_t system_parameters;
    system_parameters.TypeOfFrame             = TypeOfFrame_e::SABM;
    system_parameters.ConvergenceLayer        = 1;
    system_parameters.Priority                = 1;
    system_parameters.AckTime                 = 100; // 100ms default
    system_parameters.MaxFrameSize            = 128;
    system_parameters.MaxNumOfRetransmissions = 3; // default 3
    system_parameters.ErrRecovWindowSize      = 2; // default 2

    TS0710_DLC_ESTABL establ = TS0710_DLC_ESTABL(DLCI, system_parameters, cellular);
    pv_chanParams            = establ.getParams();

    // wait for return & set active
    active = establ.getResponse();
    LOG_DEBUG("Create channel %s: %s", pv_name.c_str(), active ? "TRUE" : "FALSE");
    responseBuffer = xMessageBufferCreate(at::defaultMessageBufferSize);
}

bool DLC_channel::init()
{
    active = establish();
    LOG_INFO("create channel %s: %s", pv_name.c_str(), active ? "TRUE" : "FALSE");

    return active;
}

DLC_channel::~DLC_channel()


@@ 54,41 48,46 @@ DLC_channel::~DLC_channel()

void DLC_channel::SendData(std::vector<uint8_t> &data)
{
    TS0710_DATA _data = TS0710_DATA(pv_DLCI, pv_chanParams, data, pv_cellular);
    TS0710_DATA _data = TS0710_DATA(pv_DLCI, chanParams, data, pv_cellular);
}

#if 0 // left here for reference
ssize_t DLC_channel::ReceiveData(std::vector<uint8_t> &data, uint32_t timeout) {
    ssize_t ret = -1;
    static uint8_t *buf = nullptr;
    buf = reinterpret_cast<uint8_t*>(malloc(pv_chanParams.MaxFrameSize));
    bool complete = false;

    while((!complete) && (timeout--)) {  //TODO: add timeout control
        ret = pv_cellular->Read(reinterpret_cast<void *>(buf), pv_chanParams.MaxFrameSize);
        if (ret > 0) {
            LOG_DEBUG("Received %i bytes", ret);
            for (int i = 0; i < ret; i++)
                data.push_back(buf[i]);
            complete = TS0710_Frame::isComplete(data);
bool DLC_channel::establish()
{
    LOG_DEBUG("Sending %s frame to DLCI %i", TypeOfFrame_text[chanParams.TypeOfFrame].c_str(), pv_DLCI);

    TS0710_Frame frame_c(TS0710_Frame::frame_t(static_cast<uint8_t>(pv_DLCI << 2) | (1 << 1),
                                               static_cast<uint8_t>(chanParams.TypeOfFrame)));

    awaitingResponseFlag.set();

    bool result = false;

    for (int retries = 0; retries < chanParams.MaxNumOfRetransmissions; ++retries) {
        pv_cellular->write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());

        auto startTime = std::chrono::steady_clock::now();
        auto endTime   = startTime + std::chrono::milliseconds{300};

        // Wait for response:
        while (true) {
            if (std::chrono::steady_clock::now() > endTime) {
                break;
            }

            if (size_t bytesRead = cmd_receive(receiveBuffer.get(), std::chrono::milliseconds{0}); bytesRead > 0) {
                auto cellularResult = bsp::cellular::CellularResult{receiveBuffer.get(), bytesRead};
                if (evaluateEstablishResponse(cellularResult)) {
                    result = true;
                    break;
                }
            }
        }
        sleep_ms(1);
    }
    if (!complete)
        LOG_ERROR("Incomplete frame received");
    if (timeout == 0)
        LOG_ERROR("Timeout occured");
    
    free(buf);

    if (!TS0710_Frame::isMyChannel(data, pv_DLCI)) {
        data.clear();
        ret = -1;
    }

    return ret;
    awaitingResponseFlag.clear();

    return result;
}
#endif

void DLC_channel::cmd_init()
{}


@@ 99,78 98,56 @@ void DLC_channel::cmd_send(std::string cmd)
    SendData(data);
}

std::string DLC_channel::cmd_receive()
size_t DLC_channel::cmd_receive(uint8_t *result, std::chrono::milliseconds timeout)
{
    cpp_freertos::LockGuard lock(mutex);
    TS0710_Frame::frame_t frame;
    std::vector<uint8_t> v(responseBuffer.begin(), responseBuffer.end());

    responseBuffer.clear();
    std::vector<std::vector<uint8_t>> mFrames;
    std::vector<uint8_t> rawBuffer;

    // get frames from buffer
    for (size_t i = 0; i < v.size(); i++) {
        rawBuffer.push_back(v[i]);
        if (/*TS0710_Frame::isComplete(rawBuffer)*/ (rawBuffer.size() > 1) && (rawBuffer[0] == 0xF9) &&
            (rawBuffer[rawBuffer.size() - 1] == 0xF9)) {
            // LOGrawBufferEBUG("Pushing back FRAME");
            mFrames.push_back(rawBuffer);
            rawBuffer.clear();
        }
    }
    // deseriaise data from received frames
    std::string deserialisedData;
    for (std::vector<uint8_t> vv : mFrames) {
        frame.deserialize(vv);
        std::string str(frame.data.begin(), frame.data.end());
        // append deserialised buffer
        deserialisedData += str;
    }
    mFrames.clear();
    return deserialisedData;
    return xMessageBufferReceive(responseBuffer, result, 2 * chanParams.MaxFrameSize, pdMS_TO_TICKS(timeout.count()));
}

void DLC_channel::cmd_post()
{}

std::vector<std::string> DLC_channel::SendCommandPrompt(const char *cmd, size_t rxCount, uint32_t timeout)
std::vector<std::string> DLC_channel::SendCommandPrompt(const char *cmd,
                                                        size_t rxCount,
                                                        std::chrono::milliseconds timeout)
{
    std::vector<std::string> tokens;

    blockedTaskHandle = xTaskGetCurrentTaskHandle();
    LOG_DEBUG("SendCommandPrompt start");

    awaitingResponseFlag.set();

    at::Result result;

    cmd_init();
    std::string cmdFixed = formatCommand(cmd);
    cmd_send(cmdFixed);

    uint32_t currentTime   = cpp_freertos::Ticks::GetTicks();
    uint32_t timeoutNeeded = timeout == UINT32_MAX ? UINT32_MAX : currentTime + timeout;
    uint32_t timeElapsed   = currentTime;

    // wait_for_data:
    while (1) {
    auto startTime = std::chrono::steady_clock::now();
    auto endTime   = startTime + timeout;

        if (timeElapsed >= timeoutNeeded) {
    LOG_DEBUG("SendCommandPrompt cmd sent");
    // Wait for response:
    while (true) {
        if (std::chrono::steady_clock::now() > endTime) {
            result.code = at::Result::Code::TIMEOUT;
            LOG_DEBUG("SendCommandPrompt cmd timeout");
            break;
        }

        auto ret    = ulTaskNotifyTake(pdTRUE, timeoutNeeded - timeElapsed);
        timeElapsed = cpp_freertos::Ticks::GetTicks();
        if (ret) {
        if (size_t bytesRead = cmd_receive(receiveBuffer.get(), std::chrono::milliseconds{0}); bytesRead > 0) {
            auto cellularResult = bsp::cellular::CellularResult{receiveBuffer.get(), bytesRead};
            auto str            = cellularResult.getDataAsString();
            LOG_DEBUG("SendCommandPrompt got response");

            std::vector<std::string> strings;
            auto cellResult = checkResult(cellularResult.getResultCode());
            if (cellResult.code != at::Result::Code::OK) {
                LOG_DEBUG("SendCommandPrompt cmd response code: %s",
                          std::string(magic_enum::enum_name(cellResult.code)).c_str());
                break;
            }

            cpp_freertos::LockGuard lock(mutex);
            TS0710_Frame::frame_t frame;
            std::vector<uint8_t> v(responseBuffer.begin(), responseBuffer.end());
            responseBuffer.clear();
            frame.deserialize(v);
            std::string str(frame.data.begin(), frame.data.end());
            // tokenize responseBuffer
            auto pos = str.find(">");
            auto pos = str.find('>');
            if (pos != std::string::npos) {
                tokens.push_back(str.substr(pos, strlen(">")));
                break;


@@ 181,31 158,42 @@ std::vector<std::string> DLC_channel::SendCommandPrompt(const char *cmd, size_t 
        }
    }

    cmd_log(cmdFixed, result, timeout);
    LOG_DEBUG("SendCommandPrompt end");
    cmdLog(cmdFixed, result, timeout);
    cmd_post();
    blockedTaskHandle = nullptr;

    awaitingResponseFlag.clear();

    return tokens;
}

int DLC_channel::ParseInputData(std::vector<uint8_t> &data)
at::Result DLC_channel::ParseInputData(bsp::cellular::CellularResult *cellularResult)
{
    at::Result result;

    cpp_freertos::LockGuard lock(mutex);

    if (blockedTaskHandle) {
        responseBuffer.append(reinterpret_cast<char *>(data.data()), data.size());
        xTaskNotifyGive(blockedTaskHandle);
    if (awaitingResponseFlag.state()) {
        if (!xMessageBufferSend(responseBuffer,
                                (void *)cellularResult->getSerialized().get(),
                                cellularResult->getSerializedSize(),
                                pdMS_TO_TICKS(at::defaultBufferTimeoutMs.count()))) {
            LOG_DEBUG("[DLC] Message buffer full!");
            result.code = at::Result::Code::FULL_MSG_BUFFER;
        }
    }
    else if (pv_callback != nullptr) {

        TS0710_Frame frame(data);
        auto deserialised = frame.getFrame().data;

        std::string receivedData = std::string(deserialised.begin(), deserialised.end());

        std::string receivedData = cellularResult->getDataAsString();
        pv_callback(receivedData);
    }
    else {
        result.code = at::Result::Code::DATA_NOT_USED;
    }

    return 1;
    return result;
}

bool DLC_channel::evaluateEstablishResponse(bsp::cellular::CellularResult &response) const
{
    auto frame = TS0710_Frame{response.getData()};
    return (frame.getFrameDLCI() == pv_DLCI &&
            (frame.getFrame().Control == (static_cast<uint8_t>(TypeOfFrame_e::UA) & ~(1 << 4))));
}

M module-cellular/Modem/TS0710/DLC_channel.h => module-cellular/Modem/TS0710/DLC_channel.h +19 -16
@@ 12,10 12,12 @@
#include <vector>
#include <functional>

#include "../ATCommon.hpp"
#include "Modem/ATCommon.hpp"
#include "TS0710_types.h"
#include <bsp/cellular/CellularResult.hpp>
#include <FreeRTOS.h>
#include <task.h>
#include <message_buffer.h>

class DLC_channel : public at::Channel
{


@@ 26,23 28,20 @@ class DLC_channel : public at::Channel
    std::string pv_name;
    DLCI_t pv_DLCI;
    bool active = false;
    DLC_ESTABL_SystemParameters_t pv_chanParams;
    DLC_ESTABL_SystemParameters_t chanParams{};
    Callback_t pv_callback;

    std::string responseBuffer;
    bsp::Cellular *pv_cellular;
    bsp::Cellular *pv_cellular{};

  public:
    // TS0710_DLC_ESTABL ctrlChanEstabl = TS0710_DLC_ESTABL(0);  //use default values to create control channel DLCI0
    DLC_channel(DLCI_t DLCI, const std::string &name, bsp::Cellular *cellular, const Callback_t &callback = nullptr);
    DLC_channel() : Channel{nullptr}, pv_name{"none"}, pv_DLCI{-1}
    {}

    DLC_channel(DLCI_t DLCI, std::string name, bsp::Cellular *cellular, Callback_t callback = nullptr);
    DLC_channel()
    {
        pv_DLCI = -1;
        pv_name = "none";
    } // default constructor creates empty channel
    virtual ~DLC_channel();

    bool init();
    bool establish();

    void SendData(std::vector<uint8_t> &data);

    DLCI_t getDLCI()


@@ 58,7 57,6 @@ class DLC_channel : public at::Channel
        return active;
    }

    // ssize_t ReceiveData(std::vector<uint8_t> &data, uint32_t timeout);
    void setCallback(Callback_t callback)
    {
        LOG_DEBUG("[%s] Setting up callback for channel", pv_name.c_str());


@@ 67,12 65,17 @@ class DLC_channel : public at::Channel

    virtual void cmd_init() override final;
    virtual void cmd_send(std::string cmd) override final;
    virtual std::string cmd_receive() override final;
    virtual size_t cmd_receive(std::uint8_t *result,
                               std::chrono::milliseconds timeout = std::chrono::milliseconds{300}) override final;
    virtual void cmd_post() override final;

    std::vector<std::string> SendCommandPrompt(const char *cmd, size_t rxCount, uint32_t timeout = 300);
    std::vector<std::string> SendCommandPrompt(const char *cmd,
                                               size_t rxCount,
                                               std::chrono::milliseconds timeout = std::chrono::milliseconds{300});

    at::Result ParseInputData(bsp::cellular::CellularResult *cellularResult);

    int ParseInputData(std::vector<uint8_t> &data);
    bool evaluateEstablishResponse(bsp::cellular::CellularResult &response) const;

    void callback(std::string &data)
    {

M module-cellular/Modem/TS0710/TS0710.cpp => module-cellular/Modem/TS0710/TS0710.cpp +187 -157
@@ 5,6 5,7 @@
#include <at/ATFactory.hpp>
#include <at/Cmd.hpp>
#include "bsp/cellular/bsp_cellular.hpp"
#include "bsp/cellular/CellularResult.hpp"
#include "projdefs.h"
#include <service-cellular/ServiceCellular.hpp>
#include <service-cellular/SignalStrength.hpp>


@@ 44,39 45,11 @@ std::map<PortSpeed_e, int> ATPortSpeeds_text          = {{PortSpeed_e::PS9600, 9
#define SERIAL_PORT "/dev/null"
#endif

#define USE_DAEFAULT_BAUDRATE 1

static const std::uint16_t threadSizeWords = 2048;
static constexpr std::uint16_t threadSizeWords = 2048;

TS0710::TS0710(PortSpeed_e portSpeed, sys::Service *parent)
{
    LOG_INFO("Serial port: '%s'", SERIAL_PORT);
    pv_portSpeed = portSpeed;
    pv_cellular  = bsp::Cellular::Create(SERIAL_PORT, 115200).value_or(nullptr);
    parser       = new ATParser(pv_cellular.get());
    pv_parent    = parent;

    // start connection
    startParams.PortSpeed               = pv_portSpeed;
    startParams.MaxFrameSize            = 127; // maximum for Basic mode
    startParams.AckTimer                = 10;  // 100ms default
    startParams.MaxNumOfRetransmissions = 3;   // default
    startParams.MaxCtrlRespTime         = 30;  // 300ms default
    startParams.WakeUpRespTime          = 10;  // 10s default
    startParams.ErrRecovWindowSize      = 2;   // 2 default

    if (auto flushed = FlushReceiveData(); flushed > 0) {
        LOG_INFO("Discarded initial %" PRIu32 " bytes sent by modem",
                 static_cast<uint32_t>(flushed)); // not baud-accurate. Might be 460800÷115200 times more
    }

    constexpr auto workerName = "TS0710SerialRxWorker";
    BaseType_t task_error =
        xTaskCreate(workerTaskFunction, workerName, threadSizeWords, this, taskPriority, &taskHandle);
    if (task_error != pdPASS) {
        LOG_ERROR("Failed to start %s task", workerName);
        return;
    }
    Init(portSpeed, parent);
}

TS0710::~TS0710()


@@ 90,9 63,47 @@ TS0710::~TS0710()
    if (taskHandle) {
        vTaskDelete(taskHandle);
    }

    delete parser;
}

void TS0710::Init(PortSpeed_e portSpeed, sys::Service *parent)
{
    SetStartParams(portSpeed);

    pv_cellular = bsp::Cellular::create(SERIAL_PORT, portSpeed).value_or(nullptr);
    parser      = new ATParser(pv_cellular.get());
    pv_parent   = parent;

    if (auto flushed = flushReceiveData(); flushed > 0) {
        LOG_INFO("Discarded initial %lu bytes sent by modem",
                 static_cast<unsigned long>(flushed)); // not baud-accurate. Might be 460800÷115200 times more
    }
    else {
        LOG_DEBUG("Nothing to discard");
    }

    constexpr auto workerName = "TS0710Worker";

    BaseType_t task_error =
        xTaskCreate(workerTaskFunction, workerName, threadSizeWords, this, taskPriority, &taskHandle);
    if (task_error != pdPASS) {
        LOG_ERROR("Failed to start %s task", workerName);
        return;
    }
}

void TS0710::SetStartParams(PortSpeed_e portSpeed)
{
    startParams.PortSpeed               = portSpeed;
    startParams.MaxFrameSize            = 127; // maximum for Basic mode
    startParams.AckTimer                = 10;  // 100ms default
    startParams.MaxNumOfRetransmissions = 3;   // default
    startParams.MaxCtrlRespTime         = 30;  // 300ms default
    startParams.WakeUpRespTime          = 10;  // 10s default
    startParams.ErrRecovWindowSize      = 2;   // 2 default
}

TS0710_Frame::frame_t createCMUXExitFrame()
{
    TS0710_Frame::frame_t frame;


@@ 145,7 156,7 @@ void CloseCmux(std::unique_ptr<bsp::Cellular> &pv_cellular)
{
    LOG_INFO("Closing mux mode");
    TS0710_Frame::frame_t frame = createCMUXExitFrame();
    pv_cellular->Write((void *)frame.serialize().data(), frame.serialize().size());
    pv_cellular->write((void *)frame.serialize().data(), frame.serialize().size());
    vTaskDelay(1000); // GSM module needs some time to close multiplexer
}



@@ 163,7 174,7 @@ TS0710::ConfState TS0710::BaudDetectOnce()
    while (!result) {
        switch (step) {
        case BaudTestStep::baud460800_NoCmux:
            pv_cellular->SetSpeed(ATPortSpeeds_text[PortSpeed_e::PS460800]);
            pv_cellular->setSpeed(ATPortSpeeds_text[PortSpeed_e::PS460800]);
            lastStep = step;
            result   = BaudDetectTestAT(parser, step, BaudTestStep::baud460800_Cmux);
            break;


@@ 173,7 184,7 @@ TS0710::ConfState TS0710::BaudDetectOnce()
            result   = BaudDetectTestAT(parser, step, BaudTestStep::baud115200_NoCmux);
            break;
        case BaudTestStep::baud115200_NoCmux:
            pv_cellular->SetSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]);
            pv_cellular->setSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]);
            lastStep = step;
            result   = BaudDetectTestAT(parser, step, BaudTestStep::baud115200_Cmux);
            break;


@@ 183,8 194,8 @@ TS0710::ConfState TS0710::BaudDetectOnce()
            result   = BaudDetectTestAT(parser, step, BaudTestStep::baud_NotFound);
            break;
        case BaudTestStep::baud_NotFound:
            pv_cellular->SetSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]); // set port speed to default 115200
            LOG_DEBUG("No Baud found for modem.");
            pv_cellular->setSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]); // set port speed to default 115200
            LOG_ERROR("No Baud found for modem.");
            return ConfState::Failure;
            break;
        }


@@ 206,31 217,25 @@ TS0710::ConfState TS0710::BaudDetectProcedure(uint16_t timeout_s)
            return ConfState::Success;
        }
    }
    pv_cellular->SetSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]); // set port speed to default 115200
    pv_cellular->setSpeed(ATPortSpeeds_text[PortSpeed_e::PS115200]); // set port speed to default 115200
    LOG_ERROR("No Baud found.");
    return ConfState::Failure;
}

// TODO:M.P Fetch configuration from JSON/XML file
TS0710::ConfState TS0710::ConfProcedure()
{
    LOG_DEBUG("Configuring modem...");

    if (!parser->cmd(at::AT::FACTORY_RESET)) {
    auto flowCmd = hardwareControlFlowEnable ? (at::AT::FLOW_CTRL_ON) : (at::AT::FLOW_CTRL_OFF);
    if (!parser->cmd(flowCmd)) {
        return ConfState::Failure;
    }
    if (!parser->cmd(at::AT::ECHO_OFF)) {

    if (!parser->cmd(at::AT::FACTORY_RESET)) {
        return ConfState::Failure;
    }

    at::AT flowCmd;
    if (hardwareControlFlowEnable) {
        flowCmd = (at::AT::FLOW_CTRL_ON);
    }
    else {
        flowCmd = (at::AT::FLOW_CTRL_OFF);
    }
    if (!parser->cmd(flowCmd)) {
    if (!parser->cmd(at::AT::ECHO_OFF)) {
        return ConfState::Failure;
    }



@@ 292,14 297,14 @@ TS0710::ConfState TS0710::AudioConfProcedure()
        if (!parser->cmd(at::AT::QMIC)) {
            return ConfState::Failure;
        }
        return SetupEchoCanceller(EchoCancellerStrength::Tuned);
        return setupEchoCanceller(EchoCancellerStrength::Tuned);
    }
    else {
        if (!parser->cmd(at::AT::QDAI_INIT)) {
            return ConfState::Failure;
        }
        else {
            pv_cellular->Restart();
            pv_cellular->restart();
            LOG_DEBUG("GSM module first run, performing reset...");
            return ConfState::ModemNeedsReset;
        }


@@ 359,13 364,13 @@ TS0710::ConfState TS0710::StartMultiplexer()
        return ConfState::Failure;
    }

    mode                          = Mode::CMUX_SETUP;
    TS0710_START *pv_TS0710_Start = new TS0710_START(TS0710_START::Mode_e::Basic, startParams, pv_cellular.get());
    mode = Mode::CMUX_SETUP;

    TS0710_START TS0710_Start{TS0710_START::Mode_e::Basic, startParams, pv_cellular.get()};
    // wait for confirmation
    if (pv_TS0710_Start->ConnectionStatus()) {
        channels.push_back(pv_TS0710_Start->getCtrlChannel()); // store control channel
    if (TS0710_Start.ConnectionStatus()) {
        channels.push_back(TS0710_Start.getCtrlChannel()); // store control channel
    }
    delete pv_TS0710_Start;

    controlCallback = [this](std::string &data) {
        auto frameData = data;


@@ 385,10 390,10 @@ TS0710::ConfState TS0710::StartMultiplexer()
                LOG_PRINTF("RTR ");
            if (frameData[3] & 0x04) {
                LOG_PRINTF("RTC ");
                this->getCellular()->SetSendingAllowed(true);
                this->getCellular()->setSendingAllowed(true);
            }
            else
                this->getCellular()->SetSendingAllowed(false);
                this->getCellular()->setSendingAllowed(false);
            if (frameData[3] & 0x02)
                LOG_PRINTF("FC ");



@@ 397,24 402,23 @@ TS0710::ConfState TS0710::StartMultiplexer()
        }
    };

    // channels[0]->setCallback(controlCallback);

    // TODO: Open remaining channels
    OpenChannel(Channel::Commands);
    OpenChannel(Channel::Notifications);
    OpenChannel(Channel::Data);
    LOG_DEBUG("[TS0710] Channels open");

    mode = Mode::CMUX;

    mode           = Mode::CMUX;
    DLC_channel *c = get(Channel::Commands);
    if (c != nullptr) {
    if (auto channel = get(Channel::Commands); channel != nullptr) {
        // Route URCs to second (Notifications) MUX channel
        c->cmd(at::AT::SET_URC_CHANNEL);
        LOG_DEBUG("[TS0710] Setting URC Channel");
        channel->cmd(at::AT::SET_URC_CHANNEL);
        LOG_DEBUG("Sending test ATI");
        auto res = c->cmd(at::AT::SW_INFO);
        auto res = channel->cmd(at::AT::SW_INFO);
        if (!res) {
            LOG_ERROR("Sending test ATI command failed");
        }
        res = c->cmd(at::AT::CSQ);
        res = channel->cmd(at::AT::CSQ);
        if (res) {
            auto beg       = res.response[0].find(" ");
            auto end       = res.response[0].find(",", 1);


@@ 443,123 447,149 @@ TS0710::ConfState TS0710::StartMultiplexer()
        return ConfState::Failure;
    }

    LOG_DEBUG("[TS0710] Mux started");
    return ConfState::Success;
}

void workerTaskFunction(void *ptr)
void TS0710::sendFrameToChannel(bsp::cellular::CellularResult &resultStruct)
{
    TS0710 *inst = reinterpret_cast<TS0710 *>(ptr);
    auto frame = TS0710_Frame{resultStruct.getData()};

    while (1) {
        auto ret = inst->pv_cellular->Wait(UINT32_MAX);
        if (ret == 0) {
            continue;
        }

        // AT mode is used only during initialization phase
        if (inst->mode == TS0710::Mode::AT) {
            // inst->atParser->ProcessNewData();
            // TODO: add AT command processing
            LOG_DEBUG("[Worker] Processing AT response");
            inst->parser->ProcessNewData(inst->pv_parent);
        }
        // CMUX mode is default operation mode
        else if (inst->mode == TS0710::Mode::CMUX) {
            // LOG_DEBUG("[Worker] Processing CMUX response");
            std::vector<uint8_t> data;
            inst->ReceiveData(data, static_cast<uint32_t>(inst->startParams.MaxCtrlRespTime));
            // send data to fifo
            for (uint8_t c : data) {
                inst->RXFifo.push(c);
    for (auto chan : getChannels()) {
        if (frame.getFrameDLCI() == chan->getDLCI()) {
            if (frame.getData().empty()) {
                // Control frame contains no data
                resultStruct.setData(frame.getSerData());
            }
            data.clear();
            // divide message to different frames as Quectel may send them one after another
            std::vector<std::vector<uint8_t>> multipleFrames;
            std::vector<uint8_t> _d;
            int fifoLen = inst->RXFifo.size();
            // LOG_DEBUG("[RXFifo] %i elements", fifoLen);

            for (int i = 0; i < fifoLen; i++) {
                _d.push_back(inst->RXFifo.front());
                inst->RXFifo.pop();
                if (/*TS0710_Frame::isComplete(_d)*/ (_d.size() > 1) && (_d[0] == 0xF9) &&
                    (_d[_d.size() - 1] == 0xF9)) {
                    // LOG_DEBUG("Pushing back FRAME");
                    multipleFrames.push_back(_d);
                    _d.clear();
                }
            }
            // if some data stored @_d then push it back to queue as incomplete packet
            if (!_d.empty() && (_d[0] == 0xF9)) {
                // LOG_DEBUG("Pushing back [%i] incomplete frame", _d.size());
                for (uint8_t c : _d)
                    inst->RXFifo.push(c);
            else {
                resultStruct.setData(frame.getData());
            }
            _d.clear();

            // LOG_DEBUG("Received %i frames", multipleFrames.size());
            for (auto *chan : inst->channels) {
                for (std::vector<uint8_t> v : multipleFrames) {
                    if (TS0710_Frame::isMyChannel(v, chan->getDLCI()))
                        chan->ParseInputData(v);
                }
            if (frame.getFrameStatus() != TS0710_Frame::OK) {
                resultStruct.setResultCode(bsp::cellular::CellularResultCode::CMUXFrameError);
            }
            multipleFrames.clear();

            chan->ParseInputData(&resultStruct);
            return;
        }
    }
}

size_t TS0710::FlushReceiveData()
void TS0710::parseCellularResultCMUX(bsp::cellular::CellularDMAResultStruct &result)
{
    using namespace std::chrono_literals;
    static std::vector<uint8_t> currentFrame;
    static bool frameStartDetected = false;

    for (auto i = 0U; i < result.dataSize; ++i) {
        uint8_t character = result.data[i];
        if (frameStartDetected || character == TS0710_FLAG) {
            currentFrame.push_back(character);

            // Check if frame is complete only in case of TS0710_FLAG
            if (frameStartDetected && character == TS0710_FLAG) {
                if (currentFrame.size() == 2) {
                    currentFrame.erase(currentFrame.begin());
                }
                if (TS0710_Frame::isComplete(currentFrame)) {
                    frameStartDetected = false;
                    bsp::cellular::CellularResult cellularResult{{result.resultCode, currentFrame}};
                    sendFrameToChannel(cellularResult);
                    currentFrame.clear();
                    continue;
                }
            }

    auto flushed                          = 0U;
    constexpr auto flushInactivityTimeout = 20ms;
    std::uint8_t dummyRead[50];
    while (pv_cellular->Wait(flushInactivityTimeout.count())) {
        flushed += pv_cellular->Read(dummyRead, sizeof(dummyRead));
            frameStartDetected = true;
        }
    }
}

size_t TS0710::flushReceiveData()
{
    auto flushed                = 0U;
    constexpr auto flushTimeout = std::chrono::milliseconds{20};
    bsp::cellular::CellularDMAResultStruct dummyResult;
    do {
        flushed += pv_cellular->read(&dummyResult, dummyResult.getMaxSize(), flushTimeout);
    } while (dummyResult.resultCode == bsp::cellular::CellularResultCode::ReceivedAndFull);
    return flushed;
}

ssize_t TS0710::ReceiveData(std::vector<uint8_t> &data, uint32_t timeout)
void TS0710::processError(bsp::cellular::CellularDMAResultStruct &result)
{
    ssize_t ret = -1;
    std::unique_ptr<uint8_t[]> buf(new uint8_t[startParams.MaxFrameSize]);
    bool complete     = false;
    uint32_t _timeout = timeout;
    bsp::cellular::CellularResult cellularResult{{result.resultCode, {}}};

    while ((!complete) && (--_timeout)) {
        ret = pv_cellular->Read(reinterpret_cast<void *>(buf.get()), startParams.MaxFrameSize);
        if (ret > 0) {
            // LOG_DEBUG("Received %i bytes", ret);
            for (int i = 0; i < ret; i++) {
                data.push_back(buf[i]);
            }
            complete = TS0710_Frame::isComplete(data);
        }
        vTaskDelay(pdMS_TO_TICKS(1));
    if (mode == TS0710::Mode::AT) {
        parser->ProcessNewData(pv_parent, cellularResult);
    }
    else if (mode == TS0710::Mode::CMUX || mode == TS0710::Mode::CMUX_SETUP) {
        sendFrameToChannel(cellularResult);
    }
}

void TS0710::processData(bsp::cellular::CellularDMAResultStruct &result)
{
    if (mode == TS0710::Mode::AT) {
        bsp::cellular::CellularResult cellularResult{result};
        parser->ProcessNewData(pv_parent, cellularResult);
    }
    if ((!complete) && (_timeout)) {
        LOG_ERROR("Incomplete frame received");
    else if (mode == TS0710::Mode::CMUX || mode == TS0710::Mode::CMUX_SETUP) {
        parseCellularResultCMUX(result);
    }
}

[[noreturn]] void workerTaskFunction(void *ptr)
{
    LOG_DEBUG("Worker start");

    constexpr auto readTimeout = std::chrono::minutes{3600};
    TS0710 *inst               = static_cast<TS0710 *>(ptr);
    bsp::cellular::CellularDMAResultStruct result{};

    while (true) {
        result.resultCode = bsp::cellular::CellularResultCode::ReceivedNoData;

        inst->pv_cellular->read(&result, bsp::cellular::CellularDMAResultStruct::getMaxSize(), readTimeout);

    return ret;
        switch (result.resultCode) {
        case bsp::cellular::CellularResultCode::ReceivedAndFull:
            LOG_DEBUG("DMA buffer full");
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivedAfterFull:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivedAndIdle:
            inst->processData(result);
            break;
        case bsp::cellular::CellularResultCode::Uninitialized:
            LOG_DEBUG("DMA uninitialized");
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::ReceivingNotStarted:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::TransmittingNotStarted:
            [[fallthrough]];
        case bsp::cellular::CellularResultCode::CMUXFrameError:
            LOG_DEBUG("CellularResult Error: %s", c_str(result.resultCode));
            inst->processError(result);
            break;
        case bsp::cellular::CellularResultCode::ReceivedNoData:
            break;
        }
    }
}

void TS0710::SelectAntenna(bsp::cellular::antenna antenna)
{
    pv_cellular->SelectAntenna(antenna);
    pv_cellular->selectAntenna(antenna);
}

bsp::cellular::antenna TS0710::GetAntenna()
{
    return pv_cellular->GetAntenna();
    return pv_cellular->getAntenna();
}

void TS0710::InformModemHostWakeup(void)
{
    return pv_cellular->InformModemHostWakeup();
    return pv_cellular->informModemHostWakeup();
}

bool TS0710::IsModemActive(void)


@@ 569,46 599,46 @@ bool TS0710::IsModemActive(void)

void TS0710::TurnOnModem(void)
{
    return pv_cellular->PowerUp();
    return pv_cellular->powerUp();
}

void TS0710::ResetModem(void)
{
    return pv_cellular->Restart();
    return pv_cellular->restart();
}

void TS0710::TurnOffModem(void)
{
    return pv_cellular->PowerDown();
    return pv_cellular->powerDown();
}

void TS0710::EnterSleepMode(void)
{
    return pv_cellular->EnterSleep();
    return pv_cellular->enterSleep();
}

void TS0710::ExitSleepMode(void)
{
    return pv_cellular->ExitSleep();
    return pv_cellular->exitSleep();
}

void TS0710::RegisterCellularDevice(void)
{
    auto deviceRegistrationMsg = std::make_shared<sys::DeviceRegistrationMessage>(pv_cellular->GetCellularDevice());
    auto deviceRegistrationMsg = std::make_shared<sys::DeviceRegistrationMessage>(pv_cellular->getCellularDevice());
    pv_parent->bus.sendUnicast(std::move(deviceRegistrationMsg), service::name::system_manager);
}

[[nodiscard]] auto TS0710::GetLastCommunicationTimestamp() const noexcept -> TickType_t
[[nodiscard]] auto TS0710::getLastCommunicationTimestamp() const noexcept -> TickType_t
{
    return pv_cellular->GetLastCommunicationTimestamp();
    return pv_cellular->getLastCommunicationTimestamp();
}

[[nodiscard]] auto TS0710::IsCellularInSleepMode() const noexcept -> bool
[[nodiscard]] auto TS0710::isCellularInSleepMode() const noexcept -> bool
{
    return pv_cellular->IsCellularInSleepMode();
    return pv_cellular->isCellularInSleepMode();
}

TS0710::ConfState TS0710::SetupEchoCanceller(EchoCancellerStrength strength)
TS0710::ConfState TS0710::setupEchoCanceller(EchoCancellerStrength strength)
{

    switch (strength) {

M module-cellular/Modem/TS0710/TS0710.h => module-cellular/Modem/TS0710/TS0710.h +38 -40
@@ 1,8 1,7 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#ifndef _TS0710_H
#define _TS0710_H
#pragma once

/*
5.2.6 Basic Option


@@ 190,6 189,9 @@ repeated until a response is obtained or action is taken by a higher layer.
*/

#include <vector>
#include <queue>
#include <string>
#include <module-bsp/bsp/cellular/bsp_cellular.hpp>

#include "TS0710_START.h"
#include "TS0710_CLOSE.h"


@@ 197,14 199,13 @@ repeated until a response is obtained or action is taken by a higher layer.
#include "TS0710_TEST.h"
#include "TS0710_WAKEUP.h"
#include "TS0710_types.h"
#include "DLC_channel.h"
#include "TS0710_Frame.h"

#include "DLC_channel.h"
#include "Modem/ATParser.hpp"
#include "Service/Service.hpp"
#include <queue>
#include <string>

#include "module-bsp/bsp/cellular/bsp_cellular.hpp"
#include <message_buffer.h>

#if defined(__cplusplus)
extern "C"


@@ 221,7 222,7 @@ namespace bsp
    class Cellular;
}

void workerTaskFunction(void *ptr);
[[noreturn]] void workerTaskFunction(void *ptr);

class TS0710
{


@@ 233,6 234,7 @@ class TS0710
        Notifications = 2,
        Data          = 3,
    };

    static std::string name(enum Channel name)
    {
        switch (name) {


@@ 247,13 249,13 @@ class TS0710
        }
        return "";
    }

    enum class Mode
    {
        AT,         /// AT raw text mode
        CMUX_SETUP, /// Modem is switching from AT to CMUX
        CMUX        /// multiplexer mode enabled
    };
    void setMode(Mode mode);

    enum class ConfState
    {


@@ 263,15 265,16 @@ class TS0710
        PowerUp
    };

    void setMode(Mode mode);

  private:
    Mode mode = Mode::AT;
    std::vector<DLC_channel *> channels;
    friend void workerTaskFunction(void *ptr);
    // worker's task handle
    xTaskHandle taskHandle      = nullptr;

    const uint32_t taskPriority = 0;
    xTaskHandle taskHandle      = nullptr;

    std::unique_ptr<bsp::Cellular> pv_cellular;
    PortSpeed_e pv_portSpeed;
    ATParser *parser;

    int CloseMultiplexer();


@@ 290,7 293,6 @@ class TS0710
    sys::Service *pv_parent;

    DLC_channel::Callback_t controlCallback = nullptr;
    std::queue<uint8_t> RXFifo;

    enum class EchoCancellerStrength
    {


@@ 299,7 301,14 @@ class TS0710
        Aggressive,
        Tuned
    };
    TS0710::ConfState SetupEchoCanceller(EchoCancellerStrength strength);

    friend void workerTaskFunction(void *ptr);
    ConfState setupEchoCanceller(EchoCancellerStrength strength);
    void parseCellularResultCMUX(bsp::cellular::CellularDMAResultStruct &result);
    size_t flushReceiveData();
    void processData(bsp::cellular::CellularDMAResultStruct &result);
    void processError(bsp::cellular::CellularDMAResultStruct &result);
    void sendFrameToChannel(bsp::cellular::CellularResult &resultStruct);

  public:
    /// @brief get Channel by index


@@ 316,31 325,22 @@ class TS0710
        return nullptr;
    }

    DLC_channel *OpenChannel(Channel chanel_val)
    std::vector<DLC_channel *> &getChannels()
    {
        DLC_channel *channel = new DLC_channel(static_cast<DLCI_t>(chanel_val), name(chanel_val), pv_cellular.get());
        channels.push_back(channel);
        return channels.back();
        return channels;
    }

    void CloseChannel(unsigned index)
    DLC_channel *OpenChannel(Channel channelIndex)
    {
        if (index >= channels.size()) {
            LOG_ERROR("Wrong channel index");
            return;
        }
        delete channels[index];
        channels.erase(channels.begin() + index);
    }
        auto *channel = new DLC_channel(static_cast<DLCI_t>(channelIndex), name(channelIndex), pv_cellular.get());
        channels.push_back(channel);

    void CloseChannel(const std::string &name)
    {
        for (size_t i = 0; i < channels.size(); i++) {
            if (channels[i]->getName() == name) {
                delete channels[i];
                channels.erase(channels.begin() + 1);
            }
        if (!channel->init()) {
            channels.pop_back();
            delete channel;
        }

        return channels.back();
    }

    void CloseChannels()


@@ 357,15 357,13 @@ class TS0710
    {
        return static_cast<DLCI_t>(channels.size() == 0 ? 0 : channels.size() - 1);
    }

    ConfState BaudDetectOnce();
    ConfState BaudDetectProcedure(uint16_t timeout_s = 30);
    ConfState ConfProcedure();
    ConfState AudioConfProcedure();
    ConfState StartMultiplexer();

    size_t FlushReceiveData();
    ssize_t ReceiveData(std::vector<uint8_t> &data, uint32_t timeout);

    bsp::Cellular *getCellular()
    {
        return pv_cellular.get();


@@ 427,6 425,8 @@ class TS0710
    TS0710(PortSpeed_e portSpeed, sys::Service *parent);
    TS0710() = delete;
    ~TS0710();
    void Init(PortSpeed_e portSpeed, sys::Service *parent);
    void SetStartParams(PortSpeed_e portSpeed);
    void SelectAntenna(bsp::cellular::antenna antenna);
    bsp::cellular::antenna GetAntenna();
    // Add error handling - only for Advanced mode. Leave for now


@@ 441,8 441,6 @@ class TS0710
    void EnterSleepMode(void);
    void ExitSleepMode(void);
    void RegisterCellularDevice(void);
    [[nodiscard]] auto GetLastCommunicationTimestamp() const noexcept -> TickType_t;
    [[nodiscard]] auto IsCellularInSleepMode() const noexcept -> bool;
    [[nodiscard]] auto getLastCommunicationTimestamp() const noexcept -> TickType_t;
    [[nodiscard]] auto isCellularInSleepMode() const noexcept -> bool;
};

#endif //_TS0710_H

M module-cellular/Modem/TS0710/TS0710_DATA.cpp => module-cellular/Modem/TS0710/TS0710_DATA.cpp +3 -3
@@ 61,7 61,7 @@ void TS0710_DATA::request(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t sysParams, 
        frame.data = User_data;
        TS0710_Frame frame_c(frame);
        // UartSend(frame_c.getSerData().data(), frame_c.getSerData().size());
        pv_cellular->Write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
        pv_cellular->write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
    }
    else { // if data size > max frame size
        int dataLeft                     = User_data.size();


@@ 76,7 76,7 @@ void TS0710_DATA::request(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t sysParams, 
            TS0710_Frame frame_c(frame);
            // UartSend(frame_c.getSerData().data(), frame_c.getSerData().size());
            // while(!pv_cellular->GetSendingAllowed());
            pv_cellular->Write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
            pv_cellular->write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
            // vTaskDelay(1);
            dataLeft -= (dataLeft <= maximumFrameLength ? dataLeft : maximumFrameLength);
        }


@@ 88,4 88,4 @@ void TS0710_DATA::request(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t sysParams, 
 * @param User_data
 */
void TS0710_DATA::indication(DLCI_t DLCI, std::vector<uint8_t> User_data)
{}
\ No newline at end of file
{}

M module-cellular/Modem/TS0710/TS0710_DLC_ESTABL.cpp => module-cellular/Modem/TS0710/TS0710_DLC_ESTABL.cpp +7 -6
@@ 21,6 21,8 @@ extern "C"
#include "log/log.hpp"
#include "FreeRTOS.h"

#include <bsp/cellular/CellularResult.hpp>

/**
 * TS0710_DLC_ESTABL implementation
 */


@@ 81,12 83,12 @@ bool TS0710_DLC_ESTABL::request(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t syste
    frame.Address = static_cast<uint8_t>(DLCI << 2) | (1 << 1); // set C/R = 1 - command
    frame.Control = static_cast<uint8_t>(system_parameters.TypeOfFrame);
    TS0710_Frame frame_c(frame);
    pv_cellular->Write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
    pv_cellular->write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
    // return true;
    int retries = system_parameters.MaxNumOfRetransmissions;
    while (retries--) {
        // UartSend(frame_c.getSerData().data(), frame_c.getSerData().size());
        pv_cellular->Write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
        pv_cellular->write(static_cast<void *>(frame_c.getSerData().data()), frame_c.getSerData().size());
        vTaskDelay(system_parameters.AckTime);
        if (response(DLCI, system_parameters)) {
            LOG_DEBUG("Got response");


@@ 113,14 115,13 @@ void TS0710_DLC_ESTABL::indication(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t sy
bool TS0710_DLC_ESTABL::response(DLCI_t DLCI, DLC_ESTABL_SystemParameters_t system_parameters)
{
    constexpr size_t size = 256;
    std::unique_ptr<uint8_t[]> data(new uint8_t[size]);
    bsp::cellular::CellularDMAResultStruct result{};

    // uint32_t len = UartReceive(data);
    ssize_t len = pv_cellular->Read(reinterpret_cast<void *>(data.get()), size);
    ssize_t len = pv_cellular->read(&result, size, std::chrono::milliseconds{0});
    LOG_DEBUG("RX length = %d", static_cast<int>(len));

    if (len > 0) {
        std::vector<uint8_t> v(data.get(), data.get() + len);
        std::vector<uint8_t> v(result.data, result.data + result.dataSize);
        TS0710_Frame frame_c(v);
        TS0710_Frame::frame_t frame = frame_c.getFrame();


M module-cellular/Modem/TS0710/TS0710_Frame.h => module-cellular/Modem/TS0710/TS0710_Frame.h +65 -19
@@ 34,12 34,26 @@ class TS0710_Frame
        0xBD, 0x2C, 0x5E, 0xCF};

  public:
    enum TS0710FrameStatus : std::uint8_t
    {
        OK,
        EmptyFrame,
        IncorrectStartStopFlags,
        CRCError,
    };

    struct frame_t
    {
        TS0710FrameStatus frameStatus = OK;
        uint8_t Address;
        uint8_t Control;
        std::vector<uint8_t> data;

        frame_t() = default;

        explicit frame_t(uint8_t address, uint8_t control) : frameStatus{OK}, Address{address}, Control{control}
        {}

        std::vector<uint8_t> serialize()
        {
            std::vector<uint8_t> ret;


@@ 65,7 79,7 @@ class TS0710_Frame
            unsigned char i   = 1;
            if (Control == static_cast<uint8_t>(TypeOfFrame_e::UIH)) {
                len = 3; // par. 5.3.6 of GSM0710 document states that for UIH frames, the FCS shall be calculated over
                         // only the address, control and length fields TODO: include 2-byte address
                // only the address, control and length fields TODO: include 2-byte address
                // if (Length > 127)
                //     len += 1;
            }


@@ 85,8 99,9 @@ class TS0710_Frame
        {
            if (serData.size() < 4) {
                LOG_ERROR("Trying to deserialize empty frame");
                Address = 0;
                Control = 0;
                Address     = 0;
                Control     = 0;
                frameStatus = EmptyFrame;
                return;
            }
            // iterate through frame to get correct trailing flag. In case multiple-frame stream provided


@@ 104,8 119,9 @@ class TS0710_Frame
            }

            if ((serData[0] != TS0710_FLAG) || (serData[myLen - 1] != TS0710_FLAG)) {
                Address = 0;
                Control = 0;
                Address     = 0;
                Control     = 0;
                frameStatus = IncorrectStartStopFlags;
                LOG_ERROR("Received frame has incorrect leading/trailing flags. Dropping.");
                return; // return empty frame. Discard frame witout proper leading & trailing flag
            }


@@ 135,7 151,7 @@ class TS0710_Frame
            unsigned char i   = 1;
            if (Control == static_cast<uint8_t>(TypeOfFrame_e::UIH)) {
                len = 3; // par. 5.3.6 of GSM0710 document states that for UIH frames, the FCS shall be calculated over
                         // only the address, control and length fields: include 2-byte address
                // only the address, control and length fields
                // if (Length > 127)
                //     len += 1;
            }


@@ 150,8 166,15 @@ class TS0710_Frame
                (Control !=
                 static_cast<uint8_t>(
                     TypeOfFrame_e::UA))) { // error - but fuck FCS check if it's faulty Quectel UIH frame or UA frame
                Address = 0;
                Control = 0;
                LOG_PRINTF("\n{");
                for (auto el : serData) {
                    LOG_PRINTF("%02X ", el);
                }
                LOG_PRINTF("}\n");

                Address     = 0;
                Control     = 0;
                frameStatus = CRCError;
                data.clear();
                LOG_ERROR("Received frame FCS [0x%02X] != 0xCF error. Dropping.", FCS);
                return; // return empty frame. Discard frame witout proper leading & trailing flag


@@ 164,33 187,42 @@ class TS0710_Frame
    std::vector<uint8_t> pv_serData;

  public:
    TS0710_Frame(frame_t frame)
    explicit TS0710_Frame(frame_t frame)
    {
        // LOG_DEBUG("Serializing given frame");
        pv_serData = frame.serialize();
        pv_frame   = frame;
    }
    TS0710_Frame(std::vector<uint8_t> &serData)

    explicit TS0710_Frame(const std::vector<uint8_t> &serData)
    {
        // LOG_DEBUG("Deserializing serData");
        pv_frame.deserialize(serData);
        pv_serData = serData;
    }

    TS0710_Frame()
    {
        LOG_DEBUG("Deserializing pv_serData");
        pv_frame.deserialize(pv_serData);
    }

    ~TS0710_Frame()
    {
        pv_serData.clear();
        pv_frame.data.clear();
    }

    frame_t getFrame()
    frame_t &getFrame()
    {
        return pv_frame;
    }

    const std::vector<uint8_t> &getData() const noexcept
    {
        return pv_frame.data;
    }

    std::vector<uint8_t> getSerData()
    {
        return pv_serData;


@@ 199,11 231,13 @@ class TS0710_Frame
    /* F9 03 3F 01 1C F9 */
    static bool isComplete(const std::vector<uint8_t> &serData)
    {
        if (serData.size() < 4)
        if (serData.size() < 4) {
            return false; // check if buffer has enough data to get length
        }

        if ((serData[0] != TS0710_FLAG) || (serData[serData.size() - 1] != TS0710_FLAG))
        if ((serData[0] != TS0710_FLAG) || (serData[serData.size() - 1] != TS0710_FLAG)) {
            return false;
        }

        int Length = 0;
        if (serData[3] & 0x01) { // short length


@@ 212,22 246,22 @@ class TS0710_Frame
        else if (serData.size() > 4) { // long length - another check if enough bytes in buffer
            Length = static_cast<uint16_t>(serData[3] >> 1) + (static_cast<uint16_t>(serData[4]) << 7);
        }
        else
        else {
            return false;
        }

        if (serData.size() >=
            static_cast<size_t>(TS0710_FRAME_HDR_LEN + Length +
                                (serData[3] & 0x01 ? 0 : 1))) // include extended address byte if present
                                (serData[3] & 0x01 ? 0 : 1))) { // include extended address byte if present
            return true;
        }

        return false;
    }

    static bool isMyChannel(const std::vector<uint8_t> &serData, DLCI_t DLCI)
    bool isMyChannel(DLCI_t DLCI) const
    {
        if ((serData.size() > 1) && ((serData[1] >> 2) == DLCI))
            return true;
        return false;
        return (pv_serData.size() > 1) && ((pv_serData[1] >> 2) == DLCI);
    }

    static DLCI_t getFrameDLCI(const std::vector<uint8_t> &serData)


@@ 236,6 270,18 @@ class TS0710_Frame
            return (serData[1] >> 2);
        return -1;
    }

    DLCI_t getFrameDLCI() const
    {
        if (pv_serData.size() > 1)
            return (pv_serData[1] >> 2);
        return -1;
    }

    TS0710FrameStatus getFrameStatus() const
    {
        return pv_frame.frameStatus;
    }
};

#endif /*_TS0710_FRAME_H*/

A module-cellular/Modem/doc/Images/at_mode.svg => module-cellular/Modem/doc/Images/at_mode.svg +72 -0
@@ 0,0 1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1324px" preserveAspectRatio="none" style="width:405px;height:1324px;" version="1.1" viewBox="0 0 405 1324" width="405px" zoomAndPan="magnify"><defs><filter height="300%" id="f1cg90pa4di60m" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacing" textLength="78" x="157" y="16.708">AT Mode</text><!--MD5=[36105e52843f3f28080f43ce369346ca]
cluster CellularBSP--><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="277" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="350" x="7" y="27.9531"/><rect fill="#FFFFFF" height="244.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="344" x="10" y="57.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="7" x2="357" y1="54.25" y2="54.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81" x="141.5" y="45.9482">CellularBSP</text><!--MD5=[4feec3315c2ae8fe0f31fdbe0936cbea]
cluster CellularWorker--><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="554" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="295" x="29" y="377.9531"/><rect fill="#FFFFFF" height="521.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="289" x="32" y="407.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="29" x2="324" y1="404.25" y2="404.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106" x="123.5" y="395.9482">CellularWorker</text><!--MD5=[df0b49b3bcea4a1034e7e56a9608b471]
cluster Channel--><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="189" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="174" x="219" y="1004.9531"/><rect fill="#FFFFFF" height="156.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="168" x="222" y="1034.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="219" x2="393" y1="1031.25" y2="1031.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="58" x="277" y="1022.9482">Channel</text><g id="CellularBSP.UartIrq"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="64.2344" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="284" x="40" y="62.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="40" x2="324" y1="89.25" y2="89.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="157.5" y="80.9482">UartIrq</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="218" x="45" y="105.3887">Reads data with DMA to static buffer</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="264" x="45" y="119.3574">and packs them into struct with result code.</text></g><g id="CellularBSP.SendToWorker"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50.2656" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="318" x="23" y="187.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="23" x2="341" y1="214.25" y2="214.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="128.5" y="205.9482">SendToWorker</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="298" x="28" y="230.3887">Puts structured data into workers MessageBuffer.</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="157.5" y="323.9482">output</text><ellipse cx="182" cy="304.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="186.3891" x2="178.6109" y1="309.3422" y2="301.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="186.3891" x2="178.6109" y1="301.564" y2="309.3422"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139" x="94.5" y="356.6514">DMAMessageBuffer</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="158" y="371.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="170" x2="170" y1="371.9531" y2="383.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="182" x2="182" y1="371.9531" y2="383.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="194" x2="194" y1="371.9531" y2="383.9531"/><g id="CellularWorker.ATParser"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50.2656" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="154" x="105" y="449.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="105" x2="259" y1="476.25" y2="476.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64" x="150" y="467.9482">ATParser</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="134" x="110" y="492.3887">Parses URC packages.</text></g><g id="CellularWorker.CmdAwaitingResponse"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="185" x="89.5" y="560.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="89.5" x2="274.5" y1="587.25" y2="587.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="165" x="99.5" y="578.9482">CmdAwaitingResponse</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="102" x="255" y="950.9482">outputWorker</text><ellipse cx="306" cy="931.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="310.3891" x2="302.6109" y1="936.3422" y2="928.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="310.3891" x2="302.6109" y1="928.564" y2="936.3422"/><g id="CellularWorker.CheckIfFOTA"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="113" x="125.5" y="687.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="125.5" x2="238.5" y1="714.25" y2="714.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="93" x="135.5" y="705.9482">CheckIfFOTA</text></g><g id="CellularWorker.StoreURC"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="89" x="45.5" y="814.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="45.5" x2="134.5" y1="841.25" y2="841.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="69" x="55.5" y="832.9482">StoreURC</text></g><g id="CellularWorker.FotaService"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="105" x="169.5" y="814.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="169.5" x2="274.5" y1="841.25" y2="841.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="85" x="179.5" y="832.9482">FotaService</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64" x="256" y="983.6514">ATBuffer</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="282" y="998.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="294" x2="294" y1="998.9531" y2="1010.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="306" x2="306" y1="998.9531" y2="1010.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="318" x2="318" y1="998.9531" y2="1010.9531"/><g id="Channel.ATStream"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50.2656" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="141" x="235.5" y="1076.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="235.5" x2="376.5" y1="1103.25" y2="1103.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="72" x="270" y="1094.9482">ATStream</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="121" x="240.5" y="1119.3887">parses AT message</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="252.5" y="1212.9482">channelOutput</text><ellipse cx="306" cy="1193.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="310.3891" x2="302.6109" y1="1198.3422" y2="1190.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="310.3891" x2="302.6109" y1="1190.564" y2="1198.3422"/><g id="ServiceCellular"><rect fill="#FEFECE" filter="url(#f1cg90pa4di60m)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="126" x="243" y="1260.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="243" x2="369" y1="1287.25" y2="1287.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106" x="253" y="1278.9482">ServiceCellular</text></g><!--MD5=[a57b3170d04d2bd212a327163f4c8137]
link UartIrq to SendToWorker--><path d="M182,127.0431 C182,144.3131 182,165.6731 182,182.6531 " fill="none" id="UartIrq-to-SendToWorker" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="182,187.7731,186,178.7731,182,182.7731,178,178.7731,182,187.7731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[12c5ee31291134eacafef3eedf3c1f48]
link SendToWorker to output--><path d="M182,238.0731 C182,256.2931 182,280.3931 182,293.8131 " fill="none" id="SendToWorker-to-output" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="182,298.8631,186,289.8631,182,293.8631,178,289.8631,182,298.8631" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[e932264d0d3e1dc4eecdd1ce9c7fd236]
link DMAMessageBuffer to ATParser--><path d="M182,384.0611 C182,395.3351 182,423.0731 182,444.7801 " fill="none" id="DMAMessageBuffer-to-ATParser" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="182,449.8731,186,440.8731,182,444.8731,178,440.8731,182,449.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[13a40670c7eb8b170560bd331fd78e1e]
link ATParser to CmdAwaitingResponse--><path d="M182,500.1441 C182,516.5131 182,538.2321 182,555.5931 " fill="none" id="ATParser-to-CmdAwaitingResponse" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="182,560.8301,186,551.8301,182,555.8301,178,551.8301,182,560.8301" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[777abd894339136b04c29cf487b6773b]
link CmdAwaitingResponse to outputWorker--><path d="M203.533,611.0751 C219.963,630.5941 242.074,659.4351 256,687.9531 C296.557,771.0041 304.253,885.5621 305.682,920.4531 " fill="none" id="CmdAwaitingResponse-to-outputWorker" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="305.872,925.6871,309.5401,916.5468,305.6891,920.6905,301.5455,916.8395,305.872,925.6871" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="29" x="289" y="781.02">True</text><!--MD5=[8a7a058cbacea9da7579bb2851695ae5]
link CmdAwaitingResponse to CheckIfFOTA--><path d="M168.072,611.1631 C163.609,620.2081 159.244,630.7631 157,640.9531 C153.871,655.1611 158.191,670.4391 164.073,683.1691 " fill="none" id="CmdAwaitingResponse-to-CheckIfFOTA" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="166.3,687.7371,165.9509,677.8945,164.1086,683.2429,158.7601,681.4007,166.3,687.7371" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="158" y="654.02">False</text><!--MD5=[fd94f81ce2f3862349c63c5a12cf90a3]
link CheckIfFOTA to StoreURC--><path d="M155.402,738.1951 C146.442,747.0751 136.758,757.5161 129,767.9531 C119.287,781.0221 110.382,796.7921 103.541,810.1471 " fill="none" id="CheckIfFOTA-to-StoreURC" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="101.117,814.9491,108.7436,808.7173,103.3702,810.4856,101.6019,805.1122,101.117,814.9491" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="130" y="781.02">False</text><!--MD5=[2bf7f23f6b2f1fdfd1cbf2b79eff56c5]
link CheckIfFOTA to FotaService--><path d="M174.406,737.9881 C171.221,752.0771 169.4,769.9881 175,784.9531 C178.492,794.2831 184.405,803.0651 190.88,810.7271 " fill="none" id="CheckIfFOTA-to-FotaService" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="194.524,814.8641,191.5787,805.466,191.2199,811.1114,185.5744,810.7526,194.524,814.8641" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="29" x="176" y="781.02">True</text><!--MD5=[460a876a9029e334c616b58fb8501c7e]
link output to DMAMessageBuffer--><path d="M182,311.0591 C182,322.7261 182,351.5151 182,366.8911 " fill="none" id="output-to-DMAMessageBuffer" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="182,371.8931,186,362.8931,182,366.8931,178,362.8931,182,371.8931" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[f66c76729024c907a3d48f760668579d]
link ATBuffer to ATStream--><path d="M306,1011.0611 C306,1022.3351 306,1050.0731 306,1071.7801 " fill="none" id="ATBuffer-to-ATStream" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="306,1076.8731,310,1067.8731,306,1071.8731,302,1067.8731,306,1076.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[dd80df3ee72f240316d7675a269eb8c4]
link ATStream to channelOutput--><path d="M306,1127.0711 C306,1145.2951 306,1169.3901 306,1182.8131 " fill="none" id="ATStream-to-channelOutput" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="306,1187.8641,310,1178.8641,306,1182.8641,302,1178.8641,306,1187.8641" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[8fe72049e9716dbce24d7677e0dd4d3e]
link outputWorker to ATBuffer--><path d="M306,938.0591 C306,949.7261 306,978.5151 306,993.8911 " fill="none" id="outputWorker-to-ATBuffer" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="306,998.8931,310,989.8931,306,993.8931,302,989.8931,306,998.8931" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[4663c35d14db62ee482ccec530d01964]
link channelOutput to ServiceCellular--><path d="M306,1200.0501 C306,1210.6081 306,1235.5673 306,1255.7077 " fill="none" id="channelOutput-to-ServiceCellular" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="306,1260.7127,310,1251.7127,306,1255.7127,302,1251.7127,306,1260.7127" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[8213d0f92b754f55ac336ef108bd4edb]
@startuml
Title AT Mode

state CellularBSP {
  UartIrq: Reads data with DMA to static buffer
  UartIrq: and packs them into struct with result code.
  UartIrq - -> SendToWorker
  SendToWorker: Puts structured data into workers MessageBuffer.
  SendToWorker - -> output

  state output <<exitPoint>>
}

state CellularWorker{
  state DMAMessageBuffer <<expansionInput>> 

  DMAMessageBuffer - -> ATParser
  ATParser: Parses URC packages.
  ATParser - -> CmdAwaitingResponse
  
  state CmdAwaitingResponse <<choice>>
  CmdAwaitingResponse - - - -> outputWorker : True
  CmdAwaitingResponse - -> CheckIfFOTA : False

  CheckIfFOTA - -> StoreURC : False
  CheckIfFOTA - -> FotaService : True

  state outputWorker <<exitPoint>>
}

output - -> DMAMessageBuffer

state Channel {
  state ATBuffer <<expansionInput>>
  
  ATBuffer - -> ATStream 
  ATStream: parses AT message
  
  ATStream - -> channelOutput

  state channelOutput <<exitPoint>>
}

outputWorker - -> ATBuffer
channelOutput - -> ServiceCellular
@enduml

PlantUML version 1.2021.4beta1(Unknown compile time)
(GPL source distribution)
Java Runtime: Java(TM) SE Runtime Environment
JVM: Java HotSpot(TM) 64-Bit Server VM
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

A module-cellular/Modem/doc/Images/at_mode.uml => module-cellular/Modem/doc/Images/at_mode.uml +46 -0
@@ 0,0 1,46 @@
@startuml
Title AT Mode

state CellularBSP {
  UartIrq: Reads data with DMA to static buffer
  UartIrq: and packs them into struct with result code.
  UartIrq --> SendToWorker
  SendToWorker: Puts structured data into workers MessageBuffer.
  SendToWorker --> output

  state output <<exitPoint>>
}

state CellularWorker{
  state DMAMessageBuffer <<expansionInput>> 

  DMAMessageBuffer --> ATParser
  ATParser: Parses URC packages.
  ATParser --> CmdAwaitingResponse
  
  state CmdAwaitingResponse <<choice>>
  CmdAwaitingResponse ----> outputWorker : True
  CmdAwaitingResponse --> CheckIfFOTA : False

  CheckIfFOTA --> StoreURC : False
  CheckIfFOTA --> FotaService : True

  state outputWorker <<exitPoint>>
}

output --> DMAMessageBuffer

state Channel {
  state ATBuffer <<expansionInput>>
  
  ATBuffer --> ATStream 
  ATStream: parses AT message
  
  ATStream --> channelOutput

  state channelOutput <<exitPoint>>
}

outputWorker --> ATBuffer
channelOutput --> ServiceCellular
@enduml

A module-cellular/Modem/doc/Images/cellular_mux_read.png => module-cellular/Modem/doc/Images/cellular_mux_read.png +0 -0
A module-cellular/Modem/doc/Images/cellular_result_struct.png => module-cellular/Modem/doc/Images/cellular_result_struct.png +0 -0
A module-cellular/Modem/doc/Images/cellular_result_struct.uml => module-cellular/Modem/doc/Images/cellular_result_struct.uml +9 -0
@@ 0,0 1,9 @@
@startuml
object CellularResultStruct {
  CellularResultCode resultCode
  vector<uint8_t> data

  +std::shared_ptr<uint8_t> serialize()
  +void deserialize(serialized, size)
}
@enduml

A module-cellular/Modem/doc/Images/cmx_mode.uml => module-cellular/Modem/doc/Images/cmx_mode.uml +70 -0
@@ 0,0 1,70 @@
@startuml
Title CMUX Mode

state CellularBSP {
  UartIrq: Reads data with DMA to static buffer
  UartIrq: and packs them into struct with result code.
  UartIrq --> SendToWorker
  SendToWorker: Puts structured data into workers MessageBuffer.
  SendToWorker --> output

  state output <<exitPoint>>
}

state CellularWorker{
  state DMAMessageBuffer <<expansionInput>> 

  DMAMessageBuffer --> ATParser
  ATParser: Parses URC packages.
  ATParser --> CmdAwaitingResponse
  
  state CmdAwaitingResponse <<choice>>
  CmdAwaitingResponse ----> workerOutput : True
  CmdAwaitingResponse --> callback : False

  state workerOutput <<exitPoint>>
}

state Notifications {
  state input1 <<expansionInput>>
  
  input1 --> ATStream1 
  
  ATStream1 --> output1

  state output1 <<exitPoint>>
}

state Commands {
  state input2 <<expansionInput>>
  
  input2 --> ATStream2 
  
  ATStream2 --> output2

  state output2 <<exitPoint>>
}

state Data {
  state input3 <<expansionInput>>
  
  input3 --> ATStream3 

  ATStream3 --> output3

  state output3 <<exitPoint>>
}

state channelsFork <<fork>>
state channelsJoin <<join>>

output --> DMAMessageBuffer : Result{data,resultCode}
workerOutput --> channelsFork
channelsFork --> input1
channelsFork --> input2
channelsFork --> input3
output1 --> channelsJoin
output2 --> channelsJoin
output3 --> channelsJoin
channelsJoin --> ServiceCellular : Result{parsedData,resultCode}
@enduml

A module-cellular/Modem/doc/Images/dma_result_struct.png => module-cellular/Modem/doc/Images/dma_result_struct.png +0 -0
A module-cellular/Modem/doc/Images/dma_result_struct.uml => module-cellular/Modem/doc/Images/dma_result_struct.uml +7 -0
@@ 0,0 1,7 @@
@startuml
object CellularDMAResultStruct {
  CellularResultCode resultCode
  size_t dataSize
  uint8_t data[CellularResultStructMaxDataSize]
}
@enduml

A module-cellular/Modem/doc/Images/mux_mode.svg => module-cellular/Modem/doc/Images/mux_mode.svg +106 -0
@@ 0,0 1,106 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" contentScriptType="application/ecmascript" contentStyleType="text/css" height="1445px" preserveAspectRatio="none" style="width:435px;height:1445px;" version="1.1" viewBox="0 0 435 1445" width="435px" zoomAndPan="magnify"><defs><filter height="300%" id="f342a3rt0b12s" width="300%" x="-1" y="-1"><feGaussianBlur result="blurOut" stdDeviation="2.0"/><feColorMatrix in="blurOut" result="blurOut2" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 .4 0"/><feOffset dx="4.0" dy="4.0" in="blurOut2" result="blurOut3"/><feBlend in="SourceGraphic" in2="blurOut3" mode="normal"/></filter></defs><g><text fill="#000000" font-family="sans-serif" font-size="18" lengthAdjust="spacing" textLength="109" x="156.5" y="16.708">CMUX Mode</text><!--MD5=[36105e52843f3f28080f43ce369346ca]
cluster CellularBSP--><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="277" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="350" x="12" y="27.9531"/><rect fill="#FFFFFF" height="244.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="344" x="15" y="57.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="12" x2="362" y1="54.25" y2="54.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81" x="146.5" y="45.9482">CellularBSP</text><!--MD5=[4feec3315c2ae8fe0f31fdbe0936cbea]
cluster CellularWorker--><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="505" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="218" x="78" y="393.9531"/><rect fill="#FFFFFF" height="472.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="212" x="81" y="423.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="78" x2="296" y1="420.25" y2="420.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106" x="134" y="411.9482">CellularWorker</text><!--MD5=[a878dacc166bb130104221e452026b76]
cluster Notifications--><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="189" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="134" x="7" y="1040.9531"/><rect fill="#FFFFFF" height="156.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="128" x="10" y="1070.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="7" x2="141" y1="1067.25" y2="1067.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="92" x="28" y="1058.9482">Notifications</text><!--MD5=[7c76d2c3a8a2b6b7f38b9d3310256c36]
cluster Commands--><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="189" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="132" x="149" y="1040.9531"/><rect fill="#FFFFFF" height="156.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="126" x="152" y="1070.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="149" x2="281" y1="1067.25" y2="1067.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80" x="175" y="1058.9482">Commands</text><!--MD5=[f21b1f164e99c6b37934f23ad048ad54]
cluster Data--><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="189" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="134" x="289" y="1040.9531"/><rect fill="#FFFFFF" height="156.7031" rx="12.5" ry="12.5" style="stroke:#FFFFFF;stroke-width:1.0;" width="128" x="292" y="1070.25"/><line style="stroke:#A80036;stroke-width:1.5;" x1="289" x2="423" y1="1067.25" y2="1067.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="34" x="339" y="1058.9482">Data</text><g id="CellularBSP.UartIrq"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="64.2344" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="284" x="45" y="62.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="45" x2="329" y1="89.25" y2="89.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="162.5" y="80.9482">UartIrq</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="218" x="50" y="105.3887">Reads data with DMA to static buffer</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="264" x="50" y="119.3574">and packs them into struct with result code.</text></g><g id="CellularBSP.SendToWorker"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50.2656" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="318" x="28" y="187.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="28" x2="346" y1="214.25" y2="214.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="107" x="133.5" y="205.9482">SendToWorker</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="298" x="33" y="230.3887">Puts structured data into workers MessageBuffer.</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="49" x="162.5" y="323.9482">output</text><ellipse cx="187" cy="304.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="191.3891" x2="183.6109" y1="309.3422" y2="301.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="191.3891" x2="183.6109" y1="301.564" y2="309.3422"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="139" x="99.5" y="372.6514">DMAMessageBuffer</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="163" y="387.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="175" x2="175" y1="387.9531" y2="399.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="187" x2="187" y1="387.9531" y2="399.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="199" x2="199" y1="387.9531" y2="399.9531"/><g id="CellularWorker.ATParser"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50.2656" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="154" x="110" y="465.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="110" x2="264" y1="492.25" y2="492.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="64" x="155" y="483.9482">ATParser</text><text fill="#000000" font-family="sans-serif" font-size="12" lengthAdjust="spacing" textLength="134" x="115" y="508.3887">Parses URC packages.</text></g><g id="CellularWorker.CmdAwaitingResponse"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="185" x="94.5" y="576.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="94.5" x2="279.5" y1="603.25" y2="603.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="165" x="104.5" y="594.9482">CmdAwaitingResponse</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="101" x="164.5" y="917.9482">workerOutput</text><ellipse cx="215" cy="898.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="219.3891" x2="211.6109" y1="903.3422" y2="895.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="219.3891" x2="211.6109" y1="895.564" y2="903.3422"/><g id="CellularWorker.callback"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="79" x="99.5" y="703.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="99.5" x2="178.5" y1="730.25" y2="730.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="59" x="109.5" y="721.9482">callback</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="47" x="50.5" y="1019.6514">input1</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="68" y="1034.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="80" x2="80" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="92" x2="92" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="104" x2="104" y1="1034.9531" y2="1046.9531"/><g id="Notifications.ATStream1"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="101" x="23.5" y="1112.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="23.5" x2="124.5" y1="1139.25" y2="1139.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81" x="33.5" y="1130.9482">ATStream1</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="58" x="72" y="1248.9482">output1</text><ellipse cx="101" cy="1229.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="105.3891" x2="97.6109" y1="1234.3422" y2="1226.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="105.3891" x2="97.6109" y1="1226.564" y2="1234.3422"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="46" x="174" y="1019.6514">input2</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="191" y="1034.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="203" x2="203" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="215" x2="215" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="227" x2="227" y1="1034.9531" y2="1046.9531"/><g id="Commands.ATStream2"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="100" x="165" y="1112.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="165" x2="265" y1="1139.25" y2="1139.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="80" x="175" y="1130.9482">ATStream2</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="57" x="186.5" y="1248.9482">output2</text><ellipse cx="215" cy="1229.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="219.3891" x2="211.6109" y1="1234.3422" y2="1226.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="219.3891" x2="211.6109" y1="1226.564" y2="1234.3422"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="47" x="296.5" y="1019.6514">input3</text><rect fill="#FEFECE" height="12" style="stroke:#A80036;stroke-width:1.5;" width="48" x="314" y="1034.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="326" x2="326" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="338" x2="338" y1="1034.9531" y2="1046.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="350" x2="350" y1="1034.9531" y2="1046.9531"/><g id="Data.ATStream3"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="101" x="305.5" y="1112.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="305.5" x2="406.5" y1="1139.25" y2="1139.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="81" x="315.5" y="1130.9482">ATStream3</text></g><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="58" x="287" y="1248.9482">output3</text><ellipse cx="316" cy="1229.9531" fill="#FEFECE" rx="6" ry="6" style="stroke:#A80036;stroke-width:1.5;"/><line style="stroke:#A80036;stroke-width:1.5;" x1="320.3891" x2="312.6109" y1="1234.3422" y2="1226.564"/><line style="stroke:#A80036;stroke-width:1.5;" x1="320.3891" x2="312.6109" y1="1226.564" y2="1234.3422"/><rect fill="#000000" filter="url(#f342a3rt0b12s)" height="8" style="stroke:none;stroke-width:1.0;" width="80" x="175" y="965.9531"/><rect fill="#000000" filter="url(#f342a3rt0b12s)" height="8" style="stroke:none;stroke-width:1.0;" width="80" x="175" y="1296.9531"/><g id="ServiceCellular"><rect fill="#FEFECE" filter="url(#f342a3rt0b12s)" height="50" rx="12.5" ry="12.5" style="stroke:#A80036;stroke-width:1.5;" width="126" x="152" y="1381.9531"/><line style="stroke:#A80036;stroke-width:1.5;" x1="152" x2="278" y1="1408.25" y2="1408.25"/><text fill="#000000" font-family="sans-serif" font-size="14" lengthAdjust="spacing" textLength="106" x="162" y="1399.9482">ServiceCellular</text></g><!--MD5=[a57b3170d04d2bd212a327163f4c8137]
link UartIrq to SendToWorker--><path d="M187,127.0431 C187,144.3131 187,165.6731 187,182.6531 " fill="none" id="UartIrq-to-SendToWorker" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="187,187.7731,191,178.7731,187,182.7731,183,178.7731,187,187.7731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[12c5ee31291134eacafef3eedf3c1f48]
link SendToWorker to output--><path d="M187,238.0731 C187,256.2931 187,280.3931 187,293.8131 " fill="none" id="SendToWorker-to-output" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="187,298.8631,191,289.8631,187,293.8631,183,289.8631,187,298.8631" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[e932264d0d3e1dc4eecdd1ce9c7fd236]
link DMAMessageBuffer to ATParser--><path d="M187,400.0631 C187,411.3331 187,439.0731 187,460.7801 " fill="none" id="DMAMessageBuffer-to-ATParser" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="187,465.8731,191,456.8731,187,460.8731,183,456.8731,187,465.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[13a40670c7eb8b170560bd331fd78e1e]
link ATParser to CmdAwaitingResponse--><path d="M187,516.1441 C187,532.5131 187,554.2321 187,571.5931 " fill="none" id="ATParser-to-CmdAwaitingResponse" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="187,576.8301,191,567.8301,187,571.8301,183,567.8301,187,576.8301" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[9a2733e9d3d9be03a03451908a1ba3d9]
link CmdAwaitingResponse to workerOutput--><path d="M189.291,627.0941 C195.068,687.9511 210.046,845.7621 214.02,887.6301 " fill="none" id="CmdAwaitingResponse-to-workerOutput" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="214.524,892.9411,217.6549,883.6032,214.0511,887.9635,209.6907,884.3598,214.524,892.9411" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="29" x="207" y="797.02">True</text><!--MD5=[d46098a13406a974ef8f13d074dfa6c4]
link CmdAwaitingResponse to callback--><path d="M153.482,626.9631 C144.458,635.3271 135.859,645.5171 131,656.9531 C125.476,669.9541 126.45,685.4391 129.233,698.6241 " fill="none" id="CmdAwaitingResponse-to-callback" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="130.427,703.7431,132.2785,694.0699,129.2914,698.8738,124.4875,695.8868,130.427,703.7431" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="33" x="132" y="670.02">False</text><!--MD5=[5af6ce0d04d61de9b9e76368ba5a826e]
link input1 to ATStream1--><path d="M91.0322,1047.0611 C88.896,1058.3351 83.6403,1086.0731 79.5275,1107.7801 " fill="none" id="input1-to-ATStream1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="78.5625,1112.8731,84.1668,1104.7742,79.4925,1107.9604,76.3064,1103.2862,78.5625,1112.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[8f490082540074d4ee2ef1a6e118265e]
link ATStream1 to output1--><path d="M81.2355,1163.0711 C86.7027,1181.2951 93.9312,1205.3901 97.9581,1218.8131 " fill="none" id="ATStream1-to-output1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="99.473,1223.8641,100.7176,1214.0942,98.036,1219.0751,93.0551,1216.3935,99.473,1223.8641" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[0988824bf8ec7c59907bef1cbce29abd]
link input2 to ATStream2--><path d="M215,1047.0611 C215,1058.3351 215,1086.0731 215,1107.7801 " fill="none" id="input2-to-ATStream2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,1112.8731,219,1103.8731,215,1107.8731,211,1103.8731,215,1112.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[6ea5f7ac2ff21e3e6e6f599ed58b1881]
link ATStream2 to output2--><path d="M215,1163.0711 C215,1181.2951 215,1205.3901 215,1218.8131 " fill="none" id="ATStream2-to-output2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,1223.8641,219,1214.8641,215,1218.8641,211,1214.8641,215,1223.8641" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[c8610ed9c90908ab582e2197c703e61b]
link input3 to ATStream3--><path d="M338.968,1047.0611 C341.104,1058.3351 346.36,1086.0731 350.473,1107.7801 " fill="none" id="input3-to-ATStream3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="351.437,1112.8731,353.6932,1103.2862,350.507,1107.9604,345.8328,1104.7742,351.437,1112.8731" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[d41c41dd4852fd322b9dd1ba5228125c]
link ATStream3 to output3--><path d="M345.281,1163.0711 C337.027,1181.6421 326.064,1206.3091 320.171,1219.5671 " fill="none" id="ATStream3-to-output3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="318.111,1224.2031,325.4202,1217.6019,320.1408,1219.6337,318.109,1214.3543,318.111,1224.2031" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[460a876a9029e334c616b58fb8501c7e]
link output to DMAMessageBuffer--><path d="M187,311.1331 C187,325.1931 187,364.2331 187,382.8431 " fill="none" id="output-to-DMAMessageBuffer" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="187,387.9031,191,378.9031,187,382.9031,183,378.9031,187,387.9031" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="160" x="188" y="354.02">Result{data,resultCode}</text><!--MD5=[613d73f2a6b0064d130614cb72f0ee69]
link workerOutput to channelsFork--><path d="M215,905.3671 C215,917.3761 215,946.3111 215,960.6341 " fill="none" id="workerOutput-to-channelsFork" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,965.7861,219,956.7861,215,960.7861,211,956.7861,215,965.7861" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[2d19209ccd932d018b2d06ef9fb86c8f]
link channelsFork to input1--><path d="M209.395,974.0971 C191.079,984.3721 132.623,1017.1641 105.751,1032.2391 " fill="none" id="channelsFork-to-input1" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="101.118,1034.8381,110.9241,1033.9212,105.4782,1032.3909,107.0085,1026.945,101.118,1034.8381" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[5fac8884c6f6b68b192a3cbb904f8716]
link channelsFork to input2--><path d="M215,974.2811 C215,984.1591 215,1013.6091 215,1029.5041 " fill="none" id="channelsFork-to-input2" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,1034.6851,219,1025.6851,215,1029.6851,211,1025.6851,215,1034.6851" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[3c5d7806c110bbdbc69ac9b51157b004]
link channelsFork to input3--><path d="M220.605,974.0971 C238.921,984.3721 297.377,1017.1641 324.249,1032.2391 " fill="none" id="channelsFork-to-input3" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="328.882,1034.8381,322.9915,1026.945,324.5218,1032.3909,319.0759,1033.9212,328.882,1034.8381" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[11b55a60e505a1d49558f29f7757b5a7]
link output1 to channelsJoin--><path d="M105.898,1233.9181 C123.325,1244.4661 182.507,1280.2861 205.632,1294.2831 " fill="none" id="output1-to-channelsJoin" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="209.991,1296.9211,204.3612,1288.84,205.713,1294.3329,200.2201,1295.6847,209.991,1296.9211" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[920d51171ddf142c6b023bfa2e85b080]
link output2 to channelsJoin--><path d="M215,1236.3671 C215,1248.3761 215,1277.3111 215,1291.6341 " fill="none" id="output2-to-channelsJoin" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,1296.7861,219,1287.7861,215,1291.7861,211,1287.7861,215,1296.7861" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[78d2c976a325450ffc8758782f6c5ecd]
link output3 to channelsJoin--><path d="M311.398,1234.0971 C295.755,1244.7841 244.462,1279.8261 223.77,1293.9621 " fill="none" id="output3-to-channelsJoin" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="219.629,1296.7901,229.3169,1295.0169,223.7578,1293.97,224.8047,1288.4108,219.629,1296.7901" style="stroke:#A80036;stroke-width:1.0;"/><!--MD5=[a54a1b5c1c5e133ff80d5fe379df8844]
link channelsJoin to ServiceCellular--><path d="M215,1304.9531 C215,1315.3821 215,1350.8308 215,1376.7961 " fill="none" id="channelsJoin-to-ServiceCellular" style="stroke:#A80036;stroke-width:1.0;"/><polygon fill="#A80036" points="215,1381.8525,219,1372.8525,215,1376.8525,211,1372.8525,215,1381.8525" style="stroke:#A80036;stroke-width:1.0;"/><text fill="#000000" font-family="sans-serif" font-size="13" lengthAdjust="spacing" textLength="206" x="216" y="1348.02">Result{parsedData,resultCode}</text><!--MD5=[1ca860d6e5dd9ee4bdeadd232ce293fa]
@startuml
Title CMUX Mode

state CellularBSP {
  UartIrq: Reads data with DMA to static buffer
  UartIrq: and packs them into struct with result code.
  UartIrq - -> SendToWorker
  SendToWorker: Puts structured data into workers MessageBuffer.
  SendToWorker - -> output

  state output <<exitPoint>>
}

state CellularWorker{
  state DMAMessageBuffer <<expansionInput>> 

  DMAMessageBuffer - -> ATParser
  ATParser: Parses URC packages.
  ATParser - -> CmdAwaitingResponse
  
  state CmdAwaitingResponse <<choice>>
  CmdAwaitingResponse - - - -> workerOutput : True
  CmdAwaitingResponse - -> callback : False

  state workerOutput <<exitPoint>>
}

state Notifications {
  state input1 <<expansionInput>>
  
  input1 - -> ATStream1 
  
  ATStream1 - -> output1

  state output1 <<exitPoint>>
}

state Commands {
  state input2 <<expansionInput>>
  
  input2 - -> ATStream2 
  
  ATStream2 - -> output2

  state output2 <<exitPoint>>
}

state Data {
  state input3 <<expansionInput>>
  
  input3 - -> ATStream3 

  ATStream3 - -> output3

  state output3 <<exitPoint>>
}

state channelsFork <<fork>>
state channelsJoin <<join>>

output - -> DMAMessageBuffer : Result{data,resultCode}
workerOutput - -> channelsFork
channelsFork - -> input1
channelsFork - -> input2
channelsFork - -> input3
output1 - -> channelsJoin
output2 - -> channelsJoin
output3 - -> channelsJoin
channelsJoin - -> ServiceCellular : Result{parsedData,resultCode}
@enduml

PlantUML version 1.2021.4beta1(Unknown compile time)
(GPL source distribution)
Java Runtime: Java(TM) SE Runtime Environment
JVM: Java HotSpot(TM) 64-Bit Server VM
Default Encoding: UTF-8
Language: en
Country: US
--></g></svg>
\ No newline at end of file

A module-cellular/Modem/doc/Images/single_cmd.uml => module-cellular/Modem/doc/Images/single_cmd.uml +24 -0
@@ 0,0 1,24 @@
@startuml
title Single cmd processing

participant IRQ order 1
participant BSP order 2
participant CellularWorker order 3
participant Channel order 4
participant Service order 5

CellularWorker -> BSP: Read()
rnote over CellularWorker: Wait for data in MessageBuffer
Service -> Channel: cmd()
activate CellularWorker
Channel -> BSP : Write()
rnote over Channel: Wait for cmd response in MessageBuffer
activate Channel
rnote over IRQ: DMA received data
IRQ -> CellularWorker: MessageBufferSendFromISR()
rnote over CellularWorker: processData()
CellularWorker -> Channel: MessageBufferSend()
rnote over Channel: Parse response
deactivate Channel
Channel -> Service: Result
@enduml

A module-cellular/Modem/doc/Images/single_cmd_transmission.png => module-cellular/Modem/doc/Images/single_cmd_transmission.png +0 -0
M module-cellular/at/Result.hpp => module-cellular/at/Result.hpp +14 -9
@@ 16,15 16,20 @@ namespace at
        /// result class for AT send -> receive command, could return promise :p
        enum class Code
        {
            OK,            /// at OK
            ERROR,         /// at ERROR For compatibility also for CME_ERROR and CMS_ERROR (details in errorCode)
            CME_ERROR,     /// In case CME error see errorCode
            CMS_ERROR,     /// In case CMS error see errorCode
            TIMEOUT,       /// at Timeout
            TOKENS,        /// at numbers of tokens needed met
            NONE,          /// no code
            UNDEFINED,     /// undefined result - usage of Undefined result, define and pin result to use it
            PARSING_ERROR, /// parser error
            OK,              /// at OK
            ERROR,           /// at ERROR For compatibility also for CME_ERROR and CMS_ERROR (details in errorCode)
            CME_ERROR,       /// In case CME error see errorCode
            CMS_ERROR,       /// In case CMS error see errorCode
            TIMEOUT,         /// at Timeout
            TOKENS,          /// at numbers of tokens needed met
            NONE,            /// no code
            UNDEFINED,       /// undefined result - usage of Undefined result, define and pin result to use it
            PARSING_ERROR,   /// parser error
            FULL_MSG_BUFFER, /// at not enough space left in message buffer for new message
            TRANSMISSION_NOT_STARTED, /// at dma not starting transmission
            RECEIVING_NOT_STARTED,    /// at dma not starting requested receiving
            DATA_NOT_USED,            /// at received data not being used
            CMUX_FRAME_ERROR,         /// at cmux deserialize error
        } code = Code::UNDEFINED;

        Result() = default;

M module-cellular/at/src/ATFactory.cpp => module-cellular/at/src/ATFactory.cpp +1 -1
@@ 28,7 28,7 @@ namespace at
        {AT::CRC_ON, {"AT+CRC=1"}},
        {AT::CALLER_NUMBER_PRESENTATION, {"AT+CLIP=1", default_long_timeout}},
        {AT::SMS_TEXT_FORMAT, {"AT+CMGF=1"}},
        {AT::SMS_UCSC2, {"AT+CSCS=\"UCS2\""}},
        {AT::SMS_UCSC2, {"AT+CSCS=\"UCS2\"", 1s}},
        {AT::SMS_GSM, {"AT+CSCS=\"GSM\""}},
        {AT::QSCLK_ON, {"AT+QSCLK=1"}},
        {AT::QDAI, {"AT+QDAI?"}},

M module-cellular/test/CMakeLists.txt => module-cellular/test/CMakeLists.txt +10 -1
@@ 46,7 46,6 @@ add_catch2_executable(
        module-cellular
)


add_catch2_executable(
        NAME
        unittest_ATURCStream


@@ 55,3 54,13 @@ add_catch2_executable(
        LIBS
        module-cellular
)

add_catch2_executable(
        NAME
        unittest_CellularResult
        SRCS
        unittest_CellularResult.cpp
        LIBS
        module-cellular
        module-bsp
)

M module-cellular/test/mock/AtCommon_channel.hpp => module-cellular/test/mock/AtCommon_channel.hpp +4 -4
@@ 41,7 41,7 @@ namespace at
        void cmd_post() override
        {}

        std::string cmd_receive() override
        size_t cmd_receive(std::uint8_t *buffer, std::chrono::milliseconds timeoutMs) override
        {
            return {};
        }


@@ 49,7 49,7 @@ namespace at

    class FailingChannel : public ChannelMock
    {
        virtual Result ResultMock()
        auto ResultMock() -> Result override
        {
            auto r = Result();
            r.code = Result::Code::ERROR;


@@ 75,7 75,7 @@ namespace at
    /// provides CSCS bad response
    class CSCS_badChannel : public ChannelMock
    {
        virtual Result ResultMock()
        auto ResultMock() -> Result override
        {
            auto r     = Result();
            r.code     = Result::Code::ERROR;


@@ 87,7 87,7 @@ namespace at
    /// standard bad CSCA values I get from modem (with result OK)
    class CSCA_emptyData : public ChannelMock
    {
        virtual Result ResultMock()
        auto ResultMock() -> Result override
        {
            auto r     = Result();
            r.code     = Result::Code::OK;

A module-cellular/test/unittest_CellularResult.cpp => module-cellular/test/unittest_CellularResult.cpp +72 -0
@@ 0,0 1,72 @@
// Copyright (c) 2017-2021, Mudita Sp. z.o.o. All rights reserved.
// For licensing, see https://github.com/mudita/MuditaOS/LICENSE.md

#define CATCH_CONFIG_MAIN

#include <catch2/catch.hpp>
#include <module-bsp/bsp/cellular/CellularResult.hpp>

TEST_CASE("CellularResult")
{
    SECTION("CellularDMAResultStruct")
    {
        bsp::cellular::CellularDMAResultStruct cellularDmaResultStruct;

        auto maxSize = bsp::cellular::CellularResultStructMaxDataSize + sizeof(cellularDmaResultStruct.resultCode) +
                       sizeof(cellularDmaResultStruct.dataSize);

        REQUIRE(cellularDmaResultStruct.getMaxSize() == maxSize);
        REQUIRE(cellularDmaResultStruct.resultCode == bsp::cellular::CellularResultCode::ReceivedNoData);
        REQUIRE(cellularDmaResultStruct.getEmptySize() == maxSize - bsp::cellular::CellularResultStructMaxDataSize);
        REQUIRE(cellularDmaResultStruct.getSize() == cellularDmaResultStruct.getEmptySize());
    }

    SECTION("Create CellularResultStruct")
    {
        bsp::cellular::CellularResultStruct cellularResultStruct;

        REQUIRE(cellularResultStruct.resultCode == bsp::cellular::CellularResultCode::Uninitialized);
        REQUIRE(cellularResultStruct.data.empty());
    }

    SECTION("Serialize empty CellularResultStruct")
    {
        bsp::cellular::CellularResultStruct cellularResultStruct;

        auto serialized       = cellularResultStruct.serialize();
        auto serializedResult = serialized.get()[0];

        delete[] serialized.release();

        REQUIRE(cellularResultStruct.getSerializedSize() == sizeof(cellularResultStruct.resultCode));
        REQUIRE(serializedResult == static_cast<unsigned char>(bsp::cellular::CellularResultCode::Uninitialized));
    }

    SECTION("Deserialize empty buffer")
    {
        bsp::cellular::CellularResultStruct cellularResultStruct;

        uint8_t serialized[0] = {};
        cellularResultStruct.deserialize(serialized, 0);

        REQUIRE(cellularResultStruct.resultCode == bsp::cellular::CellularResultCode::Uninitialized);
        REQUIRE(cellularResultStruct.data.empty());
    }

    SECTION("Serialize and deserialize CellularResultStruct")
    {
        bsp::cellular::CellularResultStruct cellularResultStruct;
        cellularResultStruct.resultCode = bsp::cellular::CellularResultCode::ReceivedAndFull;
        cellularResultStruct.data       = {1, 2, 3, 4, 5, 10};

        auto serialized = cellularResultStruct.serialize();

        bsp::cellular::CellularResultStruct cellularResultStructDeserialized;
        cellularResultStructDeserialized.deserialize(serialized.get(), cellularResultStruct.getSerializedSize());

        delete[] serialized.release();

        REQUIRE(cellularResultStructDeserialized.resultCode == cellularResultStruct.resultCode);
        REQUIRE(cellularResultStructDeserialized.data == cellularResultStruct.data);
    }
}

M module-cellular/test/unittest_cmux.cpp => module-cellular/test/unittest_cmux.cpp +1 -1
@@ 27,7 27,7 @@ TEST_CASE("TS0170 frame")
        TS0710_Frame frame(tempFrame);

        REQUIRE(frame.isComplete(frame.getSerData()) == true);
        REQUIRE(frame.isMyChannel(frame.getSerData(), DLCI) == true);
        REQUIRE(frame.isMyChannel(DLCI) == true);
        REQUIRE(frame.getFrameDLCI(frame.getSerData()) == DLCI);
        REQUIRE(frame.getSerData().size() == cmuxMinimumFrameLength + command.length());
    }

M module-services/service-cellular/ServiceCellular.cpp => module-services/service-cellular/ServiceCellular.cpp +21 -14
@@ 263,7 263,7 @@ static bool isSettingsAutomaticTimeSyncEnabled()
void ServiceCellular::SleepTimerHandler()
{
    auto currentTime                = cpp_freertos::Ticks::TicksToMs(cpp_freertos::Ticks::GetTicks());
    auto lastCommunicationTimestamp = cmux->GetLastCommunicationTimestamp();
    auto lastCommunicationTimestamp = cmux->getLastCommunicationTimestamp();
    auto timeOfInactivity           = currentTime >= lastCommunicationTimestamp
                                ? currentTime - lastCommunicationTimestamp
                                : std::numeric_limits<TickType_t>::max() - lastCommunicationTimestamp + currentTime;


@@ 372,8 372,8 @@ void ServiceCellular::registerMessageHandlers()
    connect(typeid(CellularChangeSimDataMessage), [&](sys::Message *request) -> sys::MessagePointer {
        auto msg                    = static_cast<CellularChangeSimDataMessage *>(request);
        Store::GSM::get()->selected = msg->getSim();
        bsp::cellular::sim::sim_sel();
        bsp::cellular::sim::hotswap_trigger();
        bsp::cellular::sim::simSelect();
        bsp::cellular::sim::hotSwapTrigger();
        return std::make_shared<CellularResponseMessage>(true);
    });



@@ 470,7 470,7 @@ void ServiceCellular::registerMessageHandlers()
        }
        if (typeid(*msg->event.get()) == typeid(sdesktop::developerMode::CellularSleepModeInfoRequestEvent)) {
            auto event = std::make_unique<sdesktop::developerMode::CellularSleepModeInfoRequestEvent>(
                cmux->IsCellularInSleepMode());
                cmux->isCellularInSleepMode());
            auto message = std::make_shared<sdesktop::developerMode::DeveloperModeRequest>(std::move(event));
            bus.sendUnicast(std::move(message), service::name::service_desktop);
        }


@@ 937,7 937,7 @@ bool ServiceCellular::handle_audio_conf_procedure()
            state.set(this, State::ST::Failed);
            return false;
        }
        cmux->getCellular()->SetSpeed(ATPortSpeeds_text[cmux->getStartParams().PortSpeed]);
        cmux->getCellular()->setSpeed(ATPortSpeeds_text[cmux->getStartParams().PortSpeed]);
        vTaskDelay(1000);

        if (cmux->StartMultiplexer() == TS0710::ConfState::Success) {


@@ 1316,8 1316,7 @@ auto ServiceCellular::sendSMS(SMSRecord record) -> bool
            std::string body         = UCS2(UTF8(receiver)).str();
            std::string suffix       = "\"";
            std::string command_data = command + body + suffix;
            if (cmux->CheckATCommandPrompt(
                    channel->SendCommandPrompt(command_data.c_str(), 1, commandTimeout.count()))) {
            if (cmux->CheckATCommandPrompt(channel->SendCommandPrompt(command_data.c_str(), 1, commandTimeout))) {

                if (channel->cmd((UCS2(record.body).str() + "\032").c_str(), commandTimeout)) {
                    result = true;


@@ 1355,8 1354,7 @@ auto ServiceCellular::sendSMS(SMSRecord record) -> bool
                    std::string command(at::factory(at::AT::QCMGS) + UCS2(UTF8(receiver)).str() + "\",120," +
                                        std::to_string(i + 1) + "," + std::to_string(messagePartsCount));

                    if (cmux->CheckATCommandPrompt(
                            channel->SendCommandPrompt(command.c_str(), 1, commandTimeout.count()))) {
                    if (cmux->CheckATCommandPrompt(channel->SendCommandPrompt(command.c_str(), 1, commandTimeout))) {
                        // prompt sign received, send data ended by "Ctrl+Z"
                        if (channel->cmd(UCS2(messagePart).str() + "\032", commandTimeout, 2)) {
                            result = true;


@@ 1398,6 1396,8 @@ auto ServiceCellular::sendSMS(SMSRecord record) -> bool

auto ServiceCellular::receiveSMS(std::string messageNumber) -> bool
{
    constexpr auto ucscSetMaxRetries = 3;

    auto retVal = true;

    auto channel = cmux->get(TS0710::Channel::Commands);


@@ 1406,8 1406,15 @@ auto ServiceCellular::receiveSMS(std::string messageNumber) -> bool
        return retVal;
    }

    if (!channel->cmd(at::AT::SMS_UCSC2)) {
        LOG_ERROR("Could not set UCS2 charset mode for TE");
    auto ucscSetRetries = 0;
    while (ucscSetRetries < ucscSetMaxRetries) {
        if (!channel->cmd(at::AT::SMS_UCSC2)) {
            ++ucscSetRetries;
            LOG_ERROR("Could not set UCS2 charset mode for TE. Retry %d", ucscSetRetries);
        }
        else {
            break;
        }
    }
    auto _ = gsl::finally([&channel, &retVal, &messageNumber] {
        if (!channel->cmd(at::AT::SMS_GSM)) {


@@ 1706,7 1713,7 @@ bool ServiceCellular::handle_sim_sanity_check()
    auto ret = sim_check_hot_swap(cmux->get(TS0710::Channel::Commands));
    if (ret) {
        state.set(this, State::ST::ModemOn);
        bsp::cellular::sim::sim_sel();
        bsp::cellular::sim::simSelect();
    }
    else {
        LOG_ERROR("Sanity check failure - user will be promped about full shutdown");


@@ 1718,8 1725,8 @@ bool ServiceCellular::handle_sim_sanity_check()
bool ServiceCellular::handle_select_sim()
{

    bsp::cellular::sim::sim_sel();
    bsp::cellular::sim::hotswap_trigger();
    bsp::cellular::sim::simSelect();
    bsp::cellular::sim::hotSwapTrigger();
#if defined(TARGET_Linux)
    DLC_channel *channel = cmux->get(TS0710::Channel::Commands);
    auto ret             = channel->cmd(at::AT::QSIMSTAT);

M module-services/service-evtmgr/WorkerEvent.cpp => module-services/service-evtmgr/WorkerEvent.cpp +1 -1
@@ 170,7 170,7 @@ bool WorkerEvent::handleMessage(uint32_t queueID)
        if (notification == bsp::cellular::trayPin) {
            Store::GSM::Tray pinstate = bsp::cellular::sim::getTray();
            LOG_DEBUG("SIM state change: %d", static_cast<int>(pinstate));
            bsp::cellular::sim::hotswap_trigger();
            bsp::cellular::sim::hotSwapTrigger();
        }

        if (notification == bsp::cellular::ringIndicatorPin) {

M module-utils/log/Logger.cpp => module-utils/log/Logger.cpp +2 -2
@@ 11,8 11,8 @@
namespace Log
{
    std::map<std::string, logger_level> Logger::filtered = {{"ApplicationManager", logger_level::LOGINFO},
                                                            {"TS0710Worker", logger_level::LOGINFO},
                                                            {"ServiceCellular", logger_level::LOGINFO},
                                                            {"TS0710Worker", logger_level::LOGTRACE},
                                                            {"ServiceCellular", logger_level::LOGTRACE},
                                                            {"ServiceAntenna", logger_level::LOGINFO},
                                                            {"ServiceAudio", logger_level::LOGINFO},
                                                            {"ServiceBluetooth", logger_level::LOGINFO},

M module-utils/log/debug.hpp => module-utils/log/debug.hpp +2 -2
@@ 5,10 5,10 @@

#define DEBUG_APPLICATION_MANAGEMENT 0 /// show verbose logs in ApplicationManager
#define DEBUG_SCOPED_TIMINGS         0 /// show timings in measured functions
#define _RT1051_UART_DEBUG           0 /// show full modem uart communication
#define DEBUG_CELLULAR_UART          0 /// show full modem uart communication
#define DEBUG_BLUETOOTH_HCI_COMS     0 /// show communication with BT module - transactions
#define DEBUG_BLUETOOTH_HCI_BYTES    0 /// show communication with BT module - all the HCI bytes
#define DEBUG_MODEM_OUTPUT_RESPONSE  1 /// show full modem output
#define DEBUG_MODEM_OUTPUT_RESPONSE  0 /// show full modem output
#define DEBUG_SERVICE_MESSAGES       0 /// show messages prior to handling in service
#define DEBUG_DB_MODEL_DATA          0 /// show messages prior to handling in service
#define DEBUG_FONT                   0 /// show Font debug messages