//$file${src::qs::qs_rx.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
//
// Model: qpc.qm
// File:  ${src::qs::qs_rx.c}
//
// This code has been generated by QM 5.3.0 <www.state-machine.com/qm>.
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This code is covered by the following QP license:
// License #    : LicenseRef-QL-dual
// Issued to    : Any user of the QP/C real-time embedded framework
// Framework(s) : qpc
// Support ends : 2024-12-31
// License scope:
//
// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
//
//                    Q u a n t u m  L e a P s
//                    ------------------------
//                    Modern Embedded Software
//
// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
//
// This software is dual-licensed under the terms of the open source GNU
// General Public License version 3 (or any later version), or alternatively,
// under the terms of one of the closed source Quantum Leaps commercial
// licenses.
//
// The terms of the open source GNU General Public License version 3
// can be found at: <www.gnu.org/licenses/gpl-3.0>
//
// The terms of the closed source Quantum Leaps commercial licenses
// can be found at: <www.state-machine.com/licensing>
//
// Redistributions in source code must retain this top-level comment block.
// Plagiarizing this software to sidestep the license obligations is illegal.
//
// Contact information:
// <www.state-machine.com/licensing>
// <info@state-machine.com>
//
//$endhead${src::qs::qs_rx.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#define QP_IMPL           // this is QP implementation
#include "qs_port.h"      // QS port
#include "qs_pkg.h"       // QS package-scope interface
#include "qsafe.h"        // QP Functional Safety (FuSa) Subsystem

//============================================================================
//! @cond INTERNAL

Q_DEFINE_THIS_MODULE("qs_rx")

#if (QS_OBJ_PTR_SIZE == 1U)
    typedef uint8_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 2U)
    typedef uint16_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 4U)
    typedef uint32_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 8U)
    typedef uint64_t QSObj;
#endif

typedef struct {
    uint32_t param1;
    uint32_t param2;
    uint32_t param3;
    uint8_t  idx;
    uint8_t  cmdId;
} CmdVar;

typedef struct {
    uint_fast8_t rate;
} TickVar;

typedef struct {
    uint16_t offs;
    uint8_t  size;
    uint8_t  num;
    uint8_t  idx;
} PeekVar;

typedef struct {
    uint32_t data;
    uint16_t offs;
    uint8_t  size;
    uint8_t  num;
    uint8_t  idx;
    uint8_t  fill;
} PokeVar;

typedef struct {
    uint8_t data[16];
    uint8_t idx;
    int8_t  recId; // global/local
} FltVar;

typedef struct {
    QSObj    addr;
    uint8_t  idx;
    uint8_t  kind; // see qs.h, enum QSpyObjKind
    int8_t   recId;
} ObjVar;

typedef struct {
    QEvt    *e;
    uint8_t *p;
    QSignal  sig;
    uint16_t len;
    uint8_t  prio;
    uint8_t  idx;
} EvtVar;

//! extended-state variables for the current state
//!
//! @trace
//! - @tr{DVR-QS-MC4-R19_02}
static struct {
    union Variant {
        CmdVar   cmd;
        TickVar  tick;
        PeekVar  peek;
        PokeVar  poke;
        FltVar   flt;
        ObjVar   obj;
        EvtVar   evt;
#ifdef Q_UTEST
        struct QS_TProbe tp;
#endif // Q_UTEST
    } var;
    uint8_t state;
    uint8_t esc;
    uint8_t seq;
    uint8_t chksum;
} l_rx;

enum {
    ERROR_STATE,
    WAIT4_SEQ,
    WAIT4_REC,
    WAIT4_INFO_FRAME,
    WAIT4_CMD_ID,
    WAIT4_CMD_PARAM1,
    WAIT4_CMD_PARAM2,
    WAIT4_CMD_PARAM3,
    WAIT4_CMD_FRAME,
    WAIT4_RESET_FRAME,
    WAIT4_TICK_RATE,
    WAIT4_TICK_FRAME,
    WAIT4_PEEK_OFFS,
    WAIT4_PEEK_SIZE,
    WAIT4_PEEK_NUM,
    WAIT4_PEEK_FRAME,
    WAIT4_POKE_OFFS,
    WAIT4_POKE_SIZE,
    WAIT4_POKE_NUM,
    WAIT4_POKE_DATA,
    WAIT4_POKE_FRAME,
    WAIT4_FILL_DATA,
    WAIT4_FILL_FRAME,
    WAIT4_FILTER_LEN,
    WAIT4_FILTER_DATA,
    WAIT4_FILTER_FRAME,
    WAIT4_OBJ_KIND,
    WAIT4_OBJ_ADDR,
    WAIT4_OBJ_FRAME,
    WAIT4_QUERY_KIND,
    WAIT4_QUERY_FRAME,
    WAIT4_EVT_PRIO,
    WAIT4_EVT_SIG,
    WAIT4_EVT_LEN,
    WAIT4_EVT_PAR,
    WAIT4_EVT_FRAME

#ifdef Q_UTEST
    ,
    WAIT4_TEST_SETUP_FRAME,
    WAIT4_TEST_TEARDOWN_FRAME,
    WAIT4_TEST_PROBE_DATA,
    WAIT4_TEST_PROBE_ADDR,
    WAIT4_TEST_PROBE_FRAME,
    WAIT4_TEST_CONTINUE_FRAME
#endif // Q_UTEST
};

// static helper functions...
static void QS_rxParseData_(uint8_t const b);
static void QS_rxHandleGoodFrame_(uint8_t const state);
static void QS_rxHandleBadFrame_(uint8_t const state);
static void QS_rxReportAck_(int8_t const recId);
static void QS_rxReportError_(int8_t const code);
static void QS_rxReportDone_(int8_t const recId);
static void QS_queryCurrObj(uint8_t const obj_kind);
static void QS_rxPoke_(void);

//! Internal QS-RX macro to encapsulate tran. in the QS-RX FSM
#define QS_RX_TRAN_(target_) (l_rx.state = (uint8_t)(target_))

QS_RxAttr QS_rxPriv_;

//! @endcond
//============================================================================

//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
// Check for the minimum required QP version
#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
#error qpc version 7.3.0 or higher required
#endif
//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//$define${QS::QS-RX} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

//${QS::QS-RX::rxInitBuf} ....................................................
//! @static @public @memberof QS
void QS_rxInitBuf(
    uint8_t * const sto,
    uint16_t const stoSize)
{
    QS_rxPriv_.buf  = &sto[0];
    QS_rxPriv_.end  = (QSCtr)stoSize;
    QS_rxPriv_.head = 0U;
    QS_rxPriv_.tail = 0U;

    QS_rxPriv_.currObj[SM_OBJ] = (void *)0;
    QS_rxPriv_.currObj[AO_OBJ] = (void *)0;
    QS_rxPriv_.currObj[MP_OBJ] = (void *)0;
    QS_rxPriv_.currObj[EQ_OBJ] = (void *)0;
    QS_rxPriv_.currObj[TE_OBJ] = (void *)0;
    QS_rxPriv_.currObj[AP_OBJ] = (void *)0;

    QS_RX_TRAN_(WAIT4_SEQ);
    l_rx.esc    = 0U;
    l_rx.seq    = 0U;
    l_rx.chksum = 0U;

    QS_beginRec_((uint_fast8_t)QS_OBJ_DICT);
        QS_OBJ_PRE_(&QS_rxPriv_);
        QS_STR_PRE_("QS_RX");
    QS_endRec_();
    // no QS_REC_DONE(), because QS is not running yet

    #ifdef Q_UTEST
    QS_tstPriv_.tpNum    = 0U;
    QS_tstPriv_.testTime = 0U;
    #endif // Q_UTEST
}

//${QS::QS-RX::rxParse} ......................................................
//! @static @public @memberof QS
void QS_rxParse(void) {
    // NOTE: Must be called IN critical section.
    // Also requires system-level memory access (QF_MEM_SYS()).

    QSCtr tail = QS_rxPriv_.tail;
    while (QS_rxPriv_.head != tail) { // QS-RX buffer NOT empty?
        uint8_t b = QS_rxPriv_.buf[tail];

        ++tail;
        if (tail == QS_rxPriv_.end) {
            tail = 0U;
        }
        QS_rxPriv_.tail = tail; // update the tail to a *valid* index

        if (l_rx.esc != 0U) {  // escaped byte arrived?
            l_rx.esc = 0U;
            b ^= QS_ESC_XOR;

            l_rx.chksum += b;
            QS_rxParseData_(b);
        }
        else if (b == QS_ESC) {
            l_rx.esc = 1U;
        }
        else if (b == QS_FRAME) {
            // get ready for the next frame
            b = l_rx.state; // save the current state in b
            l_rx.esc = 0U;
            QS_RX_TRAN_(WAIT4_SEQ);

            if (l_rx.chksum == QS_GOOD_CHKSUM) {
                l_rx.chksum = 0U;
                QS_rxHandleGoodFrame_(b);
            }
            else { // bad checksum
                l_rx.chksum = 0U;
                QS_rxReportError_(0x41);
                QS_rxHandleBadFrame_(b);
            }
        }
        else {
            l_rx.chksum += b;
            QS_rxParseData_(b);
        }
    }
}

//${QS::QS-RX::rxGetNfree} ...................................................
//! @static @public @memberof QS
uint16_t QS_rxGetNfree(void) {
    // NOTE: Must be called IN critical section.
    // Also requires system-level memory access (QF_MEM_SYS()).

    QSCtr const head = QS_rxPriv_.head;
    uint16_t nFree;
    if (head == QS_rxPriv_.tail) { // buffer empty?
        nFree = (uint16_t)(QS_rxPriv_.end - 1U);
    }
    else if (head < QS_rxPriv_.tail) {
        nFree = (uint16_t)(QS_rxPriv_.tail - (head + 1U));
    }
    else {
        nFree = (uint16_t)((head + 1U) - QS_rxPriv_.tail);
        nFree = (uint16_t)(QS_rxPriv_.end - nFree);
    }
    return nFree;
}
//$enddef${QS::QS-RX} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

//============================================================================
//! @cond INTERNAL

static void QS_rxParseData_(uint8_t const b) {
    switch (l_rx.state) {
        case (uint8_t)WAIT4_SEQ: {
            ++l_rx.seq;
            if (l_rx.seq != b) {
                QS_rxReportError_(0x42);
                l_rx.seq = b; // update the sequence
            }
            QS_RX_TRAN_(WAIT4_REC);
            break;
        }
        case (uint8_t)WAIT4_REC: {
            switch (b) {
                case (uint8_t)QS_RX_INFO:
                    QS_RX_TRAN_(WAIT4_INFO_FRAME);
                    break;
                case (uint8_t)QS_RX_COMMAND:
                    QS_RX_TRAN_(WAIT4_CMD_ID);
                    break;
                case (uint8_t)QS_RX_RESET:
                    QS_RX_TRAN_(WAIT4_RESET_FRAME);
                    break;
                case (uint8_t)QS_RX_TICK:
                    QS_RX_TRAN_(WAIT4_TICK_RATE);
                    break;
                case (uint8_t)QS_RX_PEEK:
                    if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) {
                        l_rx.var.peek.offs = 0U;
                        l_rx.var.peek.idx  = 0U;
                        QS_RX_TRAN_(WAIT4_PEEK_OFFS);
                    }
                    else {
                        QS_rxReportError_((int8_t)QS_RX_PEEK);
                        QS_RX_TRAN_(ERROR_STATE);
                    }
                    break;
                case (uint8_t)QS_RX_POKE: // intentionally fall-through
                case (uint8_t)QS_RX_FILL:
                    l_rx.var.poke.fill =
                            ((b == (uint8_t)QS_RX_FILL) ? 1U : 0U);
                    if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) {
                        l_rx.var.poke.offs = 0U;
                        l_rx.var.poke.idx  = 0U;
                        QS_RX_TRAN_(WAIT4_POKE_OFFS);
                    }
                    else {
                        QS_rxReportError_((l_rx.var.poke.fill != 0U)
                                           ? (int8_t)QS_RX_FILL
                                           : (int8_t)QS_RX_POKE);
                        QS_RX_TRAN_(ERROR_STATE);
                    }
                    break;
                case (uint8_t)QS_RX_GLB_FILTER: // intentionally fall-through
                case (uint8_t)QS_RX_LOC_FILTER:
                    l_rx.var.flt.recId = (int8_t)b;
                    QS_RX_TRAN_(WAIT4_FILTER_LEN);
                    break;
                case (uint8_t)QS_RX_AO_FILTER: // intentionally fall-through
                case (uint8_t)QS_RX_CURR_OBJ:
                    l_rx.var.obj.recId = (int8_t)b;
                    QS_RX_TRAN_(WAIT4_OBJ_KIND);
                    break;
                case (uint8_t)QS_RX_QUERY_CURR:
                    l_rx.var.obj.recId = (int8_t)QS_RX_QUERY_CURR;
                    QS_RX_TRAN_(WAIT4_QUERY_KIND);
                    break;
                case (uint8_t)QS_RX_EVENT:
                    QS_RX_TRAN_(WAIT4_EVT_PRIO);
                    break;

#ifdef Q_UTEST
                case (uint8_t)QS_RX_TEST_SETUP:
                    QS_RX_TRAN_(WAIT4_TEST_SETUP_FRAME);
                    break;
                case (uint8_t)QS_RX_TEST_TEARDOWN:
                    QS_RX_TRAN_(WAIT4_TEST_TEARDOWN_FRAME);
                    break;
                case (uint8_t)QS_RX_TEST_CONTINUE:
                    QS_RX_TRAN_(WAIT4_TEST_CONTINUE_FRAME);
                    break;
                case (uint8_t)QS_RX_TEST_PROBE:
                    if (QS_tstPriv_.tpNum
                        < (uint8_t)(sizeof(QS_tstPriv_.tpBuf)
                                    / sizeof(QS_tstPriv_.tpBuf[0])))
                    {
                        l_rx.var.tp.data = 0U;
                        l_rx.var.tp.idx  = 0U;
                        QS_RX_TRAN_(WAIT4_TEST_PROBE_DATA);
                    }
                    else { // the # Test-Probes exceeded
                        QS_rxReportError_((int8_t)QS_RX_TEST_PROBE);
                        QS_RX_TRAN_(ERROR_STATE);
                    }
                    break;
#endif // Q_UTEST

                default:
                    QS_rxReportError_(0x43);
                    QS_RX_TRAN_(ERROR_STATE);
                    break;
            }
            break;
        }
        case (uint8_t)WAIT4_INFO_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_CMD_ID: {
            l_rx.var.cmd.cmdId  = b;
            l_rx.var.cmd.idx    = 0U;
            l_rx.var.cmd.param1 = 0U;
            l_rx.var.cmd.param2 = 0U;
            l_rx.var.cmd.param3 = 0U;
            QS_RX_TRAN_(WAIT4_CMD_PARAM1);
            break;
        }
        case (uint8_t)WAIT4_CMD_PARAM1: {
            l_rx.var.cmd.param1 |= ((uint32_t)b << l_rx.var.cmd.idx);
            l_rx.var.cmd.idx    += 8U;
            if (l_rx.var.cmd.idx == (8U * 4U)) {
                l_rx.var.cmd.idx = 0U;
                QS_RX_TRAN_(WAIT4_CMD_PARAM2);
            }
            break;
        }
        case (uint8_t)WAIT4_CMD_PARAM2: {
            l_rx.var.cmd.param2 |= ((uint32_t)b << l_rx.var.cmd.idx);
            l_rx.var.cmd.idx    += 8U;
            if (l_rx.var.cmd.idx == (8U * 4U)) {
                l_rx.var.cmd.idx = 0U;
                QS_RX_TRAN_(WAIT4_CMD_PARAM3);
            }
            break;
        }
        case (uint8_t)WAIT4_CMD_PARAM3: {
            l_rx.var.cmd.param3 |= ((uint32_t)b << l_rx.var.cmd.idx);
            l_rx.var.cmd.idx    += 8U;
            if (l_rx.var.cmd.idx == (8U * 4U)) {
                l_rx.var.cmd.idx = 0U;
                QS_RX_TRAN_(WAIT4_CMD_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_CMD_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_RESET_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_TICK_RATE: {
            l_rx.var.tick.rate = (uint_fast8_t)b;
            QS_RX_TRAN_(WAIT4_TICK_FRAME);
            break;
        }
        case (uint8_t)WAIT4_TICK_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_PEEK_OFFS: {
            if (l_rx.var.peek.idx == 0U) {
                l_rx.var.peek.offs = (uint16_t)b;
                l_rx.var.peek.idx += 8U;
            }
            else {
                l_rx.var.peek.offs |= (uint16_t)((uint16_t)b << 8U);
                QS_RX_TRAN_(WAIT4_PEEK_SIZE);
            }
            break;
        }
        case (uint8_t)WAIT4_PEEK_SIZE: {
            if ((b == 1U) || (b == 2U) || (b == 4U)) {
                l_rx.var.peek.size = b;
                QS_RX_TRAN_(WAIT4_PEEK_NUM);
            }
            else {
                QS_rxReportError_((int8_t)QS_RX_PEEK);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_PEEK_NUM: {
            l_rx.var.peek.num = b;
            QS_RX_TRAN_(WAIT4_PEEK_FRAME);
            break;
        }
        case (uint8_t)WAIT4_PEEK_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_POKE_OFFS: {
            if (l_rx.var.poke.idx == 0U) {
                l_rx.var.poke.offs = (uint16_t)b;
                l_rx.var.poke.idx  = 1U;
            }
            else {
                l_rx.var.poke.offs |= (uint16_t)((uint16_t)b << 8U);
                QS_RX_TRAN_(WAIT4_POKE_SIZE);
            }
            break;
        }
        case (uint8_t)WAIT4_POKE_SIZE: {
            if ((b == 1U) || (b == 2U) || (b == 4U)) {
                l_rx.var.poke.size = b;
                QS_RX_TRAN_(WAIT4_POKE_NUM);
            }
            else {
                QS_rxReportError_((l_rx.var.poke.fill != 0U)
                                  ? (int8_t)QS_RX_FILL
                                  : (int8_t)QS_RX_POKE);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_POKE_NUM: {
            if (b > 0U) {
                l_rx.var.poke.num  = b;
                l_rx.var.poke.data = 0U;
                l_rx.var.poke.idx  = 0U;
                QS_RX_TRAN_((l_rx.var.poke.fill != 0U)
                            ? WAIT4_FILL_DATA
                            : WAIT4_POKE_DATA);
            }
            else {
                QS_rxReportError_((l_rx.var.poke.fill != 0U)
                                  ? (int8_t)QS_RX_FILL
                                  : (int8_t)QS_RX_POKE);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_FILL_DATA: {
            l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx);
            l_rx.var.poke.idx += 8U;
            if ((uint8_t)(l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) {
                QS_RX_TRAN_(WAIT4_FILL_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_POKE_DATA: {
            l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx);
            l_rx.var.poke.idx += 8U;
            if ((uint8_t)(l_rx.var.poke.idx >> 3U) == l_rx.var.poke.size) {
                QS_rxPoke_();
                --l_rx.var.poke.num;
                if (l_rx.var.poke.num == 0U) {
                    QS_RX_TRAN_(WAIT4_POKE_FRAME);
                }
            }
            break;
        }
        case (uint8_t)WAIT4_FILL_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case WAIT4_POKE_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_FILTER_LEN: {
            if (b == sizeof(l_rx.var.flt.data)) {
                l_rx.var.flt.idx = 0U;
                QS_RX_TRAN_(WAIT4_FILTER_DATA);
            }
            else {
                QS_rxReportError_(l_rx.var.flt.recId);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_FILTER_DATA: {
            l_rx.var.flt.data[l_rx.var.flt.idx] = b;
            ++l_rx.var.flt.idx;
            if (l_rx.var.flt.idx == sizeof(l_rx.var.flt.data)) {
                QS_RX_TRAN_(WAIT4_FILTER_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_FILTER_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_OBJ_KIND: {
            if (b <= (uint8_t)SM_AO_OBJ) {
                l_rx.var.obj.kind = b;
                l_rx.var.obj.addr = 0U;
                l_rx.var.obj.idx  = 0U;
                QS_RX_TRAN_(WAIT4_OBJ_ADDR);
            }
            else {
                QS_rxReportError_(l_rx.var.obj.recId);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_OBJ_ADDR: {
            l_rx.var.obj.addr |= ((QSObj)b << l_rx.var.obj.idx);
            l_rx.var.obj.idx += 8U;
            if (l_rx.var.obj.idx == (uint8_t)(8U * QS_OBJ_PTR_SIZE)) {
                QS_RX_TRAN_(WAIT4_OBJ_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_OBJ_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_QUERY_KIND: {
            if (b < (uint8_t)MAX_OBJ) {
                l_rx.var.obj.kind = b;
                QS_RX_TRAN_(WAIT4_QUERY_FRAME);
            }
            else {
                QS_rxReportError_(l_rx.var.obj.recId);
                QS_RX_TRAN_(ERROR_STATE);
            }
            break;
        }
        case (uint8_t)WAIT4_QUERY_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_EVT_PRIO: {
            l_rx.var.evt.prio = b;
            l_rx.var.evt.sig  = 0U;
            l_rx.var.evt.idx  = 0U;
            QS_RX_TRAN_(WAIT4_EVT_SIG);
            break;
        }
        case (uint8_t)WAIT4_EVT_SIG: {
            l_rx.var.evt.sig |= (QSignal)((uint32_t)b << l_rx.var.evt.idx);
            l_rx.var.evt.idx += 8U;
            if (l_rx.var.evt.idx == (uint8_t)(8U * Q_SIGNAL_SIZE)) {
                l_rx.var.evt.len = 0U;
                l_rx.var.evt.idx = 0U;
                QS_RX_TRAN_(WAIT4_EVT_LEN);
            }
            break;
        }
        case (uint8_t)WAIT4_EVT_LEN: {
            l_rx.var.evt.len |= (uint16_t)((uint32_t)b << l_rx.var.evt.idx);
            l_rx.var.evt.idx += 8U;
            if (l_rx.var.evt.idx == (8U * 2U)) {
                if ((l_rx.var.evt.len + sizeof(QEvt)) <=
                    QF_poolGetMaxBlockSize())
                {
                    // report Ack before generating any other QS records
                    QS_rxReportAck_((int8_t)QS_RX_EVENT);

                    l_rx.var.evt.e = QF_newX_(
                        ((uint_fast16_t)l_rx.var.evt.len + sizeof(QEvt)),
                        0U, // margin
                        (enum_t)l_rx.var.evt.sig);
                    if (l_rx.var.evt.e != (QEvt *)0) { // evt allocated?
                        l_rx.var.evt.p = (uint8_t *)l_rx.var.evt.e;
                        l_rx.var.evt.p = &l_rx.var.evt.p[sizeof(QEvt)];
                        if (l_rx.var.evt.len > 0U) {
                            QS_RX_TRAN_(WAIT4_EVT_PAR);
                        }
                        else {
                            QS_RX_TRAN_(WAIT4_EVT_FRAME);
                        }
                    }
                    else {
                        QS_rxReportError_((int8_t)QS_RX_EVENT);
                        QS_RX_TRAN_(ERROR_STATE);
                    }
                }
                else {
                    QS_rxReportError_((int8_t)QS_RX_EVENT);
                    QS_RX_TRAN_(ERROR_STATE);
                }
            }
            break;
        }
        case (uint8_t)WAIT4_EVT_PAR: { // event parameters
            *l_rx.var.evt.p = b;
            ++l_rx.var.evt.p;
            --l_rx.var.evt.len;
            if (l_rx.var.evt.len == 0U) {
                QS_RX_TRAN_(WAIT4_EVT_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_EVT_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }

#ifdef Q_UTEST
        case (uint8_t)WAIT4_TEST_SETUP_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_TEST_TEARDOWN_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_TEST_CONTINUE_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
        case (uint8_t)WAIT4_TEST_PROBE_DATA: {
            l_rx.var.tp.data |= ((uint32_t)b << l_rx.var.tp.idx);
            l_rx.var.tp.idx += 8U;
            if (l_rx.var.tp.idx == (uint8_t)(8U * sizeof(uint32_t))) {
                l_rx.var.tp.addr = 0U;
                l_rx.var.tp.idx  = 0U;
                QS_RX_TRAN_(WAIT4_TEST_PROBE_ADDR);
            }
            break;
        }
        case (uint8_t)WAIT4_TEST_PROBE_ADDR: {
            l_rx.var.tp.addr |= ((QSFun)b << l_rx.var.tp.idx);
            l_rx.var.tp.idx += 8U;
            if (l_rx.var.tp.idx == (uint8_t)(8U * QS_FUN_PTR_SIZE)) {
                QS_RX_TRAN_(WAIT4_TEST_PROBE_FRAME);
            }
            break;
        }
        case (uint8_t)WAIT4_TEST_PROBE_FRAME: {
            // keep ignoring the data until a frame is collected
            break;
        }
#endif // Q_UTEST

        case (uint8_t)ERROR_STATE: {
            // keep ignoring the data until a good frame is collected
            break;
        }
        default: {  // unexpected or unimplemented state
            QS_rxReportError_(0x45);
            QS_RX_TRAN_(ERROR_STATE);
            break;
        }
    }
}

//............................................................................
static void QS_rxHandleGoodFrame_(uint8_t const state) {
    uint8_t i;
    uint8_t *ptr;

    switch (state) {
        case WAIT4_INFO_FRAME: {
            // no need to report Ack or Done
            QS_target_info_pre_(0U); // send only Target info
            break;
        }
        case WAIT4_RESET_FRAME: {
            // no need to report Ack or Done, because Target resets
            QS_onReset(); // reset the Target
            break;
        }
        case WAIT4_CMD_PARAM1: // intentionally fall-through
        case WAIT4_CMD_PARAM2: // intentionally fall-through
        case WAIT4_CMD_PARAM3: // intentionally fall-through
        case WAIT4_CMD_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_COMMAND);
            QS_onCommand(l_rx.var.cmd.cmdId, l_rx.var.cmd.param1,
                         l_rx.var.cmd.param2, l_rx.var.cmd.param3);
#ifdef Q_UTEST
    #if Q_UTEST != 0
            QS_processTestEvts_(); // process all events produced
    #endif  // Q_UTEST != 0
#endif  // Q_UTEST
            QS_rxReportDone_((int8_t)QS_RX_COMMAND);
            break;
        }
        case WAIT4_TICK_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_TICK);
#ifdef Q_UTEST
            QTimeEvt_tick1_((uint_fast8_t)l_rx.var.tick.rate, &QS_rxPriv_);
    #if Q_UTEST != 0
            QS_processTestEvts_(); // process all events produced
    #endif  // Q_UTEST != 0
#else
            QTimeEvt_tick_((uint_fast8_t)l_rx.var.tick.rate, &QS_rxPriv_);
#endif  // Q_UTEST
            QS_rxReportDone_((int8_t)QS_RX_TICK);
            break;
        }
        case WAIT4_PEEK_FRAME: {
            // no need to report Ack or Done
            QS_beginRec_((uint_fast8_t)QS_PEEK_DATA);
                ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ];
                ptr = &ptr[l_rx.var.peek.offs];
                QS_TIME_PRE_();                  // timestamp
                QS_U16_PRE_(l_rx.var.peek.offs); // data offset
                QS_U8_PRE_(l_rx.var.peek.size);  // data size
                QS_U8_PRE_(l_rx.var.peek.num);   // # data items
                for (i = 0U; i < l_rx.var.peek.num; ++i) {
                    switch (l_rx.var.peek.size) {
                        case 1:
                            QS_U8_PRE_(ptr[i]);
                            break;
                        case 2:
                            QS_U16_PRE_(((uint16_t *)ptr)[i]);
                            break;
                        case 4:
                            QS_U32_PRE_(((uint32_t *)ptr)[i]);
                            break;
                        default:
                            // intentionally empty
                            break;
                    }
                }
            QS_endRec_();

            QS_REC_DONE(); // user callback (if defined)
            break;
        }
        case WAIT4_POKE_DATA: {
            // received less than expected poke data items
            QS_rxReportError_((int8_t)QS_RX_POKE);
            break;
        }
        case WAIT4_POKE_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_POKE);
            // no need to report done
            break;
        }
        case WAIT4_FILL_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_FILL);
            ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ];
            ptr = &ptr[l_rx.var.poke.offs];
            for (i = 0U; i < l_rx.var.poke.num; ++i) {
                switch (l_rx.var.poke.size) {
                    case 1:
                        ptr[i] = (uint8_t)l_rx.var.poke.data;
                        break;
                    case 2:
                        ((uint16_t *)ptr)[i]
                            = (uint16_t)l_rx.var.poke.data;
                        break;
                    case 4:
                        ((uint32_t *)ptr)[i] = l_rx.var.poke.data;
                        break;
                    default:
                        // intentionally empty
                        break;
                }
            }
            break;
        }
        case WAIT4_FILTER_FRAME: {
            QS_rxReportAck_(l_rx.var.flt.recId);

            // apply the received filters
            if (l_rx.var.flt.recId == (int8_t)QS_RX_GLB_FILTER) {
                for (i = 0U; i < Q_DIM(QS_filt_.glb); ++i) {
                    QS_filt_.glb[i] = l_rx.var.flt.data[i];
                }
                // leave the "not maskable" filters enabled,
                // see qs.h, Miscellaneous QS records (not maskable)
                QS_filt_.glb[0] |= 0x01U;
                QS_filt_.glb[7] |= 0xFCU;
                QS_filt_.glb[8] |= 0x7FU;

                // never enable the last 3 records (0x7D, 0x7E, 0x7F)
                QS_filt_.glb[15] &= 0x1FU;
            }
            else if (l_rx.var.flt.recId == (int8_t)QS_RX_LOC_FILTER) {
                for (i = 0U; i < Q_DIM(QS_filt_.loc); ++i) {
                    QS_filt_.loc[i] = l_rx.var.flt.data[i];
                }
                // leave QS_ID == 0 always on
                QS_filt_.loc[0] |= 0x01U;
            }
            else {
                QS_rxReportError_(l_rx.var.flt.recId);
            }
            // no need to report Done
            break;
        }
        case WAIT4_OBJ_FRAME: {
            i = l_rx.var.obj.kind;
            if (i < (uint8_t)MAX_OBJ) {
                if (l_rx.var.obj.recId == (int8_t)QS_RX_CURR_OBJ) {
                    QS_rxPriv_.currObj[i] = (void *)l_rx.var.obj.addr;
                    QS_rxReportAck_((int8_t)QS_RX_CURR_OBJ);
                }
                else if (l_rx.var.obj.recId == (int8_t)QS_RX_AO_FILTER) {
                    if (l_rx.var.obj.addr != 0U) {
                        int_fast16_t const filter =
                           (int_fast16_t)((QActive *)l_rx.var.obj.addr)->prio;
                        QS_locFilter_((i == 0U)
                            ? filter
                            :-filter);
                        QS_rxReportAck_((int8_t)QS_RX_AO_FILTER);
                    }
                    else {
                        QS_rxReportError_((int8_t)QS_RX_AO_FILTER);
                    }
                }
                else {
                    QS_rxReportError_(l_rx.var.obj.recId);
                }
            }
            // both SM and AO
            else if (i == (uint8_t)SM_AO_OBJ) {
                if (l_rx.var.obj.recId == (int8_t)QS_RX_CURR_OBJ) {
                    QS_rxPriv_.currObj[SM_OBJ] = (void *)l_rx.var.obj.addr;
                    QS_rxPriv_.currObj[AO_OBJ] = (void *)l_rx.var.obj.addr;
                }
                QS_rxReportAck_(l_rx.var.obj.recId);
            }
            else {
                QS_rxReportError_(l_rx.var.obj.recId);
            }
            break;
        }
        case WAIT4_QUERY_FRAME: {
            QS_queryCurrObj(l_rx.var.obj.kind);
            break;
        }
        case WAIT4_EVT_FRAME: {
            // NOTE: Ack was already reported in the WAIT4_EVT_LEN state
#ifdef Q_UTEST
            QS_onTestEvt(l_rx.var.evt.e); // adjust the event, if needed
#endif // Q_UTEST
            i = 0U; // use 'i' as status, 0 == success,no-recycle

            if (l_rx.var.evt.prio == 0U) { // publish
                QActive_publish_(l_rx.var.evt.e, &QS_rxPriv_, 0U);
            }
            else if (l_rx.var.evt.prio < QF_MAX_ACTIVE) {
                if (!QACTIVE_POST_X(QActive_registry_[l_rx.var.evt.prio],
                               l_rx.var.evt.e,
                               0U, // margin
                               &QS_rxPriv_))
                {
                    // failed QACTIVE_POST() recycles the event
                    i = 0x80U; // failure status, no recycle
                }
            }
            else if (l_rx.var.evt.prio == 255U) { // special prio
                // dispatch to the current SM object
                if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) {
                    // increment the ref-ctr to simulate the situation
                    // when the event is just retreived from a queue.
                    // This is expected for the following QF_gc() call.
                    ++l_rx.var.evt.e->refCtr_;

                    QAsm * const sm = (QAsm *)QS_rxPriv_.currObj[SM_OBJ];
                    (*sm->vptr->dispatch)(sm, l_rx.var.evt.e, 0U);
                    i = 0x01U;  // success status, recycle needed
                }
                else {
                    i = 0x81U;  // failure status, recycle needed
                }
            }
            else if (l_rx.var.evt.prio == 254U) { // special prio
                // init the current SM object"
                if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) {
                    // increment the ref-ctr to simulate the situation
                    // when the event is just retreived from a queue.
                    // This is expected for the following QF_gc() call.
                    ++l_rx.var.evt.e->refCtr_;

                    QAsm * const sm = (QAsm *)QS_rxPriv_.currObj[SM_OBJ];
                    (*sm->vptr->init)(sm, l_rx.var.evt.e, 0U);
                    i = 0x01U;  // success status, recycle needed
                }
                else {
                    i = 0x81U;  // failure status, recycle needed
                }
            }
            else if (l_rx.var.evt.prio == 253U) { // special prio
                // post to the current AO
                if (QS_rxPriv_.currObj[AO_OBJ] != (void *)0) {
                    if (!QACTIVE_POST_X(
                            (QActive *)QS_rxPriv_.currObj[AO_OBJ],
                            l_rx.var.evt.e,
                            0U, // margin
                            &QS_rxPriv_))
                    {
                        // failed QACTIVE_POST() recycles the event
                        i = 0x80U;  // failure status, no recycle
                    }
                }
                else {
                    i = 0x81U;  // failure status, recycle needed
                }
            }
            else {
                i = 0x81U;  // failure status, recycle needed
            }

#if (QF_MAX_EPOOL > 0U)
            if ((i & 0x01U) != 0U) { // recycle needed?
                QF_gc(l_rx.var.evt.e);
            }
#endif
            if ((i & 0x80U) != 0U) { // failure?
                QS_rxReportError_((int8_t)QS_RX_EVENT);
            }
            else {
#ifdef Q_UTEST
    #if Q_UTEST != 0
                QS_processTestEvts_(); // process all events produced
    #endif  // Q_UTEST != 0
#endif  // Q_UTEST
                QS_rxReportDone_((int8_t)QS_RX_EVENT);
            }
            break;
        }

#ifdef Q_UTEST
        case WAIT4_TEST_SETUP_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_TEST_SETUP);
            QS_tstPriv_.tpNum    = 0U; // clear the Test-Probes
            QS_tstPriv_.testTime = 0U; // clear the time tick
            // don't clear current objects
            QS_onTestSetup(); // application-specific test setup
            // no need to report Done
            break;
        }
        case WAIT4_TEST_TEARDOWN_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_TEST_TEARDOWN);
            QS_onTestTeardown(); // application-specific test teardown
            // no need to report Done
            break;
        }
        case WAIT4_TEST_CONTINUE_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_TEST_CONTINUE);
            QS_rxPriv_.inTestLoop = false; // exit the QUTest loop
            // no need to report Done
            break;
        }
        case WAIT4_TEST_PROBE_FRAME: {
            QS_rxReportAck_((int8_t)QS_RX_TEST_PROBE);
            Q_ASSERT_INCRIT(815, QS_tstPriv_.tpNum
                < (sizeof(QS_tstPriv_.tpBuf) / sizeof(QS_tstPriv_.tpBuf[0])));
            QS_tstPriv_.tpBuf[QS_tstPriv_.tpNum] = l_rx.var.tp;
            ++QS_tstPriv_.tpNum;
            // no need to report Done
            break;
        }
#endif // Q_UTEST

        case ERROR_STATE: {
            // keep ignoring all bytes until new frame
            break;
        }
        default: {
            QS_rxReportError_(0x47);
            break;
        }
    }
}

//............................................................................
static void QS_rxHandleBadFrame_(uint8_t const state) {
    QS_rxReportError_(0x50); // report error for all bad frames
    switch (state) {
        case WAIT4_EVT_FRAME: {
            Q_ASSERT_INCRIT(910, l_rx.var.evt.e != (QEvt *)0);
#if (QF_MAX_EPOOL > 0U)
            QF_gc(l_rx.var.evt.e); // don't leak an allocated event
#endif
            break;
        }
        default: {
            // intentionally empty
            break;
        }
    }
}

//............................................................................
static void QS_rxReportAck_(int8_t const recId) {
    QS_beginRec_((uint_fast8_t)QS_RX_STATUS);
        QS_U8_PRE_(recId); // record ID
    QS_endRec_();
    QS_REC_DONE(); // user callback (if defined)
}

//............................................................................
static void QS_rxReportError_(int8_t const code) {
    QS_beginRec_((uint_fast8_t)QS_RX_STATUS);
        QS_U8_PRE_(0x80U | (uint8_t)code); // error code
    QS_endRec_();
    QS_REC_DONE(); // user callback (if defined)
}

//............................................................................
static void QS_rxReportDone_(int8_t const recId) {
    QS_beginRec_((uint_fast8_t)QS_TARGET_DONE);
        QS_TIME_PRE_();    // timestamp
        QS_U8_PRE_(recId); // record ID
    QS_endRec_();
    QS_REC_DONE(); // user callback (if defined)
}

//............................................................................
static void QS_queryCurrObj(uint8_t const obj_kind) {
    if (QS_rxPriv_.currObj[obj_kind] != (void *)0) {
        QS_beginRec_((uint_fast8_t)QS_QUERY_DATA);
            QS_TIME_PRE_();       // timestamp
            QS_U8_PRE_(obj_kind); // object kind
            QS_OBJ_PRE_(QS_rxPriv_.currObj[obj_kind]);
            switch (obj_kind) {
                case (uint8_t)SM_OBJ: // intentionally fall through
                case (uint8_t)AO_OBJ:
                    QS_FUN_PRE_((*((QAsm *)QS_rxPriv_.currObj[obj_kind])->vptr
                                ->getStateHandler)(
                                     ((QAsm *)QS_rxPriv_.currObj[obj_kind])));
                    break;
                case (uint8_t)MP_OBJ:
                    QS_MPC_PRE_(((QMPool *)QS_rxPriv_.currObj[obj_kind])
                                ->nFree);
                    QS_MPC_PRE_(((QMPool *)QS_rxPriv_.currObj[obj_kind])
                                ->nMin);
                    break;
                case (uint8_t)EQ_OBJ:
                    QS_EQC_PRE_(((QEQueue *)QS_rxPriv_.currObj[obj_kind])
                                ->nFree);
                    QS_EQC_PRE_(((QEQueue *)QS_rxPriv_.currObj[obj_kind])
                                ->nMin);
                    break;
                case (uint8_t)TE_OBJ:
                    QS_OBJ_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind])
                                ->act);
                    QS_TEC_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind])
                                ->ctr);
                    QS_TEC_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind])
                                ->interval);
                    QS_SIG_PRE_(((QTimeEvt *)QS_rxPriv_.currObj[obj_kind])
                                ->super.sig);
                    QS_U8_PRE_ (((QTimeEvt *)QS_rxPriv_.currObj[obj_kind])
                                ->super.refCtr_);
                    break;
                default:
                    // intentionally empty
                    break;
            }
        QS_endRec_();
        QS_REC_DONE(); // user callback (if defined)
    }
    else {
        QS_rxReportError_((int8_t)QS_RX_QUERY_CURR);
    }
}

//............................................................................
static void QS_rxPoke_(void) {
    uint8_t *ptr = (uint8_t *)QS_rxPriv_.currObj[AP_OBJ];
    ptr = &ptr[l_rx.var.poke.offs];
    switch (l_rx.var.poke.size) {
        case 1:
            *ptr = (uint8_t)l_rx.var.poke.data;
            break;
        case 2:
            *(uint16_t *)ptr = (uint16_t)l_rx.var.poke.data;
            break;
        case 4:
            *(uint32_t *)ptr = l_rx.var.poke.data;
            break;
        default: {
            Q_ERROR_INCRIT(900);
            break;
        }
    }

    l_rx.var.poke.data = 0U;
    l_rx.var.poke.idx  = 0U;
    l_rx.var.poke.offs += (uint16_t)l_rx.var.poke.size;
}

//! @endcond
