qpc/source/qs_rx.c
Quantum Leaps 28ef7ac6ce 5.9.0
2017-05-17 13:16:32 -04:00

1165 lines
40 KiB
C

/**
* @file
* @brief QS/C receive channel services
* @ingroup qs
* @cond
******************************************************************************
* Last updated for version 5.9.0
* Last updated on 2017-05-17
*
* Q u a n t u m L e a P s
* ---------------------------
* innovating embedded systems
*
* Copyright (C) 2005-2017 Quantum Leaps, LLC. All rights reserved.
*
* This program is open source software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published
* by the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Alternatively, this program may be distributed and modified under the
* terms of Quantum Leaps commercial licenses, which expressly supersede
* the GNU General Public License and are specifically designed for
* licensees interested in retaining the proprietary status of their code.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Contact information:
* https://state-machine.com
* mailto:info@state-machine.com
******************************************************************************
* @endcond
*/
#include "qs_port.h" /* QS port */
#include "qs_pkg.h" /* QS package-scope interface */
#include "qassert.h" /* QP embedded systems-friendly assertions */
Q_DEFINE_THIS_MODULE("qs_rx")
/****************************************************************************/
QSrxPriv QS_rxPriv_; /* QS-RX private data */
/****************************************************************************/
#if (QS_OBJ_PTR_SIZE == 1)
typedef uint8_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 2)
typedef uint16_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 4)
typedef uint32_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 8)
typedef uint64_t QSObj;
#endif
#if (QS_FUN_PTR_SIZE == 1)
typedef uint8_t QSFun;
#elif (QS_FUN_PTR_SIZE == 2)
typedef uint16_t QSFun;
#elif (QS_FUN_PTR_SIZE == 4)
typedef uint32_t QSFun;
#elif (QS_FUN_PTR_SIZE == 8)
typedef uint64_t QSFun;
#endif
/** @cond
* Exlcude the following internals from the Doxygen documentation
* Extended-state variables used for parsing various QS-RX Records
*/
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;
} GFltVar;
typedef struct {
QSObj addr;
uint8_t idx;
uint8_t kind; /* see qs.h, enum QSpyObjKind */
uint8_t recId;
} ObjVar;
typedef struct {
QSFun addr;
uint32_t data;
uint8_t idx;
} TPVar; /* Test-Probe */
typedef struct {
uint8_t prio;
} AFltVar;
typedef struct {
QEvt *e;
uint8_t *p;
QSignal sig;
uint16_t len;
uint8_t prio;
uint8_t idx;
} EvtVar;
static struct {
union Variant {
CmdVar cmd;
TickVar tick;
PeekVar peek;
PokeVar poke;
GFltVar gFlt;
AFltVar aFlt;
ObjVar obj;
EvtVar evt;
TPVar tp;
} var; /* extended-state variables for the current state */
uint8_t state;
uint8_t esc;
uint8_t seq;
uint8_t chksum;
} l_rx;
enum {
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_GLB_FILTER_LEN,
WAIT4_GLB_FILTER_DATA,
WAIT4_GLB_FILTER_FRAME,
WAIT4_AO_FILTER_PRIO,
WAIT4_AO_FILTER_FRAME,
WAIT4_OBJ_KIND,
WAIT4_OBJ_ADDR,
WAIT4_OBJ_FRAME,
WAIT4_EVT_PRIO,
WAIT4_EVT_SIG,
WAIT4_EVT_LEN,
WAIT4_EVT_PAR,
WAIT4_EVT_FRAME,
WAIT4_TEST_SETUP_FRAME,
WAIT4_TEST_TEARDOWN_FRAME,
WAIT4_TEST_PROBE_DATA,
WAIT4_TEST_PROBE_ADDR,
WAIT4_TEST_PROBE_FRAME,
WAIT4_TEST_CONTINUE_FRAME,
ERROR_STATE
};
#ifdef Q_UTEST
static struct {
TPVar tpBuf[16]; /* buffer of Test-Probes received so far */
uint8_t tpNum; /* current number of Test-Probes */
QSTimeCtr testTime; /* test time (tick counter) */
} l_testData;
#endif /* Q_UTEST */
/* static helper functions... */
static void QS_rxParseData_(uint8_t b);
static void QS_rxHandleGoodFrame_(uint8_t state);
static void QS_rxHandleBadFrame_(uint8_t state);
static void QS_rxReportAck_(enum QSpyRxRecords recId);
static void QS_rxReportError_(uint8_t code);
static void QS_rxReportDone_(enum QSpyRxRecords recId);
static void QS_rxPoke_(void);
static uint8_t const l_QS_RX = (uint8_t)0; /* QS source ID */
/*! Internal QS-RX macro to access the QS ring buffer */
/**
* @description
* The QS-RX buffer is allocated by the user and is accessed through the
* pointer QS_rxPriv_.buf, which violates the MISRA-C 2004 Rule 17.4(req),
* pointer arithmetic other than array indexing. Encapsulating this violation
* in a macro allows to selectively suppress this specific deviation.
*/
#define QS_RX_AT_(i_) (QS_rxPriv_.buf + (i_))
/*! Internal QS-RX macro to encapsulate transition in the QS-RX FSM */
#define QS_RX_TRAN_(target_) (l_rx.state = (uint8_t)(target_))
/** @endcond */
/****************************************************************************/
/**
* @description
* This function should be called from QS_onStartup() to provide QS-RX with
* the receive data buffer.
*
* @param[in] sto[] the address of the memory block
* @param[in] stoSize the size of this block [bytes]. The size of the
* QS RX buffer cannot exceed 64KB.
*
* @note QS-RX can work with quite small data buffers, but you will start
* losing data if the buffer is not drained fast enough in the idle task.
*
* @note If the data input rate exceeds the QS-RX processing rate, the data
* will be lost, but the QS protocol will notice that:
* (1) that the checksum in the incomplete QS records will fail; and
* (2) the sequence counter in QS records will show discontinuities.
*
* The QS-RX channel will report any data errors by sending the
* QS_RX_DATA_ERROR trace record.
*/
void QS_rxInitBuf(uint8_t sto[], uint16_t stoSize) {
QS_rxPriv_.buf = &sto[0];
QS_rxPriv_.end = (QSCtr)stoSize - (QSCtr)1;
QS_rxPriv_.head = (QSCtr)0;
QS_rxPriv_.tail = (QSCtr)0;
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 = (uint8_t)0U;
l_rx.seq = (uint8_t)0;
l_rx.chksum = (uint8_t)0;
QS_beginRec((uint_fast8_t)QS_OBJ_DICT);
QS_OBJ_(&l_QS_RX);
QS_STR_("QS_RX");
QS_endRec();
/* no QS_REC_DONE(), because QS is not running yet */
#ifdef Q_UTEST
l_testData.tpNum = (uint8_t)0;
l_testData.testTime = (QSTimeCtr)0;
#endif /* Q_UTEST */
}
/****************************************************************************/
/**
* @description
* This function is intended to be called from the ISR that reads the QS-RX
* bytes from the QSPY host application. The function returns the conservative
* number of free bytes currently available in the buffer, assuming that
* the head pointer is not being moved concurrently. The tail pointer might
* be moving, meaning that bytes can be concurrently removed from the buffer.
*/
uint16_t QS_rxGetNfree(void) {
uint16_t nFree;
if (QS_rxPriv_.head == QS_rxPriv_.tail) {
nFree = (uint16_t)QS_rxPriv_.end;
}
else if (QS_rxPriv_.head < QS_rxPriv_.tail) {
nFree = (uint16_t)(QS_rxPriv_.tail - QS_rxPriv_.head);
}
else {
nFree = (uint16_t)((QS_rxPriv_.tail + QS_rxPriv_.end)
- QS_rxPriv_.head);
}
return nFree;
}
/****************************************************************************/
void QS_rxParse(void) {
while (QS_rxPriv_.head != QS_rxPriv_.tail) { /* QS-RX buffer not empty? */
uint8_t b = *QS_RX_AT_(QS_rxPriv_.tail);
if (QS_rxPriv_.tail != (QSCtr)0) {
--QS_rxPriv_.tail;
}
else {
QS_rxPriv_.tail = QS_rxPriv_.end;
}
if (l_rx.esc) { /* escaped byte arrived? */
l_rx.esc = (uint8_t)0;
b ^= QS_ESC_XOR;
l_rx.chksum += b;
QS_rxParseData_(b);
}
else if (b == QS_ESC) {
l_rx.esc = (uint8_t)1;
}
else if (b == QS_FRAME) {
/* get ready for the next frame */
b = l_rx.state; /* save the current state in b */
l_rx.esc = (uint8_t)0;
QS_RX_TRAN_(WAIT4_SEQ);
if (l_rx.chksum == QS_GOOD_CHKSUM) {
l_rx.chksum = (uint8_t)0;
QS_rxHandleGoodFrame_(b);
}
else { /* bad checksum */
l_rx.chksum = (uint8_t)0;
QS_rxReportError_((uint8_t)0x41U);
QS_rxHandleBadFrame_(b);
}
}
else {
l_rx.chksum += b;
QS_rxParseData_(b);
}
}
}
/****************************************************************************/
static void QS_rxParseData_(uint8_t b) {
switch (l_rx.state) {
case WAIT4_SEQ: {
++l_rx.seq;
if (l_rx.seq != b) {
QS_rxReportError_((uint8_t)0x42U);
l_rx.seq = b; /* update the sequence */
}
QS_RX_TRAN_(WAIT4_REC);
break;
}
case WAIT4_REC: {
switch (b) {
case QS_RX_INFO:
QS_RX_TRAN_(WAIT4_INFO_FRAME);
break;
case QS_RX_COMMAND:
QS_RX_TRAN_(WAIT4_CMD_ID);
break;
case QS_RX_RESET:
QS_RX_TRAN_(WAIT4_RESET_FRAME);
break;
case QS_RX_TICK:
QS_RX_TRAN_(WAIT4_TICK_RATE);
break;
case QS_RX_PEEK:
if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) {
l_rx.var.peek.offs = (uint16_t)0;
l_rx.var.peek.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_PEEK_OFFS);
}
else {
QS_rxReportError_((uint8_t)QS_RX_PEEK);
QS_RX_TRAN_(ERROR_STATE);
}
break;
case QS_RX_POKE:
case QS_RX_FILL:
l_rx.var.poke.fill = (b == (uint8_t)QS_RX_FILL)
? (uint8_t)1
: (uint8_t)0;
if (QS_rxPriv_.currObj[AP_OBJ] != (void *)0) {
l_rx.var.poke.offs = (uint16_t)0;
l_rx.var.poke.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_POKE_OFFS);
}
else {
QS_rxReportError_((l_rx.var.poke.fill != (uint8_t)0)
? (uint8_t)QS_RX_FILL
: (uint8_t)QS_RX_FILL);
QS_RX_TRAN_(ERROR_STATE);
}
break;
case QS_RX_GLB_FILTER:
QS_RX_TRAN_(WAIT4_GLB_FILTER_LEN);
break;
case QS_RX_LOC_FILTER:
l_rx.var.obj.recId = (uint8_t)QS_RX_LOC_FILTER;
QS_RX_TRAN_(WAIT4_OBJ_KIND);
break;
case QS_RX_AO_FILTER:
QS_RX_TRAN_(WAIT4_AO_FILTER_PRIO);
break;
case QS_RX_CURR_OBJ:
l_rx.var.obj.recId = (uint8_t)QS_RX_CURR_OBJ;
QS_RX_TRAN_(WAIT4_OBJ_KIND);
break;
case QS_RX_EVENT:
QS_RX_TRAN_(WAIT4_EVT_PRIO);
break;
#ifdef Q_UTEST
case QS_RX_TEST_SETUP:
QS_RX_TRAN_(WAIT4_TEST_SETUP_FRAME);
break;
case QS_RX_TEST_TEARDOWN:
QS_RX_TRAN_(WAIT4_TEST_TEARDOWN_FRAME);
break;
case QS_RX_TEST_CONTINUE:
QS_RX_TRAN_(WAIT4_TEST_CONTINUE_FRAME);
break;
case QS_RX_TEST_PROBE:
if (l_testData.tpNum
< (uint8_t)(sizeof(l_testData.tpBuf)
/sizeof(l_testData.tpBuf[0])))
{
l_rx.var.tp.data = (uint32_t)0;
l_rx.var.tp.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_TEST_PROBE_DATA);
}
else { /* the number of Test-Probes exceeded */
QS_rxReportError_((uint8_t)QS_RX_TEST_PROBE);
QS_RX_TRAN_(ERROR_STATE);
}
break;
#endif /* Q_UTEST */
default:
QS_rxReportError_((uint8_t)0x43U);
QS_RX_TRAN_(ERROR_STATE);
break;
}
break;
}
case WAIT4_INFO_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_CMD_ID: {
l_rx.var.cmd.cmdId = b;
l_rx.var.cmd.idx = (uint8_t)0;
l_rx.var.cmd.param1 = (uint32_t)0;
l_rx.var.cmd.param2 = (uint32_t)0;
l_rx.var.cmd.param3 = (uint32_t)0;
QS_RX_TRAN_(WAIT4_CMD_PARAM1);
break;
}
case WAIT4_CMD_PARAM1: {
l_rx.var.cmd.param1 |= ((uint32_t)b << l_rx.var.cmd.idx);
l_rx.var.cmd.idx += (uint8_t)8;
if (l_rx.var.cmd.idx == (uint8_t)(8*4)) {
l_rx.var.cmd.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_CMD_PARAM2);
}
break;
}
case WAIT4_CMD_PARAM2: {
l_rx.var.cmd.param2 |= ((uint32_t)b << l_rx.var.cmd.idx);
l_rx.var.cmd.idx += (uint8_t)8;
if (l_rx.var.cmd.idx == (uint8_t)(8*4)) {
l_rx.var.cmd.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_CMD_PARAM3);
}
break;
}
case WAIT4_CMD_PARAM3: {
l_rx.var.cmd.param3 |= ((uint32_t)b << l_rx.var.cmd.idx);
l_rx.var.cmd.idx += (uint8_t)8;
if (l_rx.var.cmd.idx == (uint8_t)(8*4)) {
l_rx.var.cmd.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_CMD_FRAME);
}
break;
}
case WAIT4_CMD_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_RESET_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_TICK_RATE: {
l_rx.var.tick.rate = (uint_fast8_t)b;
QS_RX_TRAN_(WAIT4_TICK_FRAME);
break;
}
case WAIT4_TICK_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_PEEK_OFFS: {
if (l_rx.var.peek.idx == (uint8_t)0) {
l_rx.var.peek.offs = (uint16_t)b;
l_rx.var.peek.idx += (uint8_t)8;
}
else {
l_rx.var.peek.offs |= (uint16_t)((uint16_t)b << 8);
QS_RX_TRAN_(WAIT4_PEEK_SIZE);
}
break;
}
case WAIT4_PEEK_SIZE: {
if ((b == (uint8_t)1) || (b == (uint8_t)2) || (b == (uint8_t)4)) {
l_rx.var.peek.size = b;
QS_RX_TRAN_(WAIT4_PEEK_NUM);
}
else {
QS_rxReportError_((uint8_t)QS_RX_PEEK);
QS_RX_TRAN_(ERROR_STATE);
}
break;
}
case WAIT4_PEEK_NUM: {
l_rx.var.peek.num = b;
QS_RX_TRAN_(WAIT4_PEEK_FRAME);
break;
}
case WAIT4_PEEK_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_POKE_OFFS: {
if (l_rx.var.poke.idx == (uint8_t)0) {
l_rx.var.poke.offs = (uint16_t)b;
l_rx.var.poke.idx = (uint8_t)1;
}
else {
l_rx.var.poke.offs |= (uint16_t)((uint16_t)b << 8);
QS_RX_TRAN_(WAIT4_POKE_SIZE);
}
break;
}
case WAIT4_POKE_SIZE: {
if ((b == (uint8_t)1) || (b == (uint8_t)2) || (b == (uint8_t)4)) {
l_rx.var.poke.size = b;
QS_RX_TRAN_(WAIT4_POKE_NUM);
}
else {
QS_rxReportError_((l_rx.var.poke.fill != (uint8_t)0)
? (uint8_t)QS_RX_FILL
: (uint8_t)QS_RX_POKE);
QS_RX_TRAN_(ERROR_STATE);
}
break;
}
case WAIT4_POKE_NUM: {
if (b > (uint8_t)0) {
l_rx.var.poke.num = b;
l_rx.var.poke.data = (uint32_t)0;
l_rx.var.poke.idx = (uint8_t)0;
QS_RX_TRAN_((l_rx.var.poke.fill != (uint8_t)0)
? WAIT4_FILL_DATA
: WAIT4_POKE_DATA);
}
else {
QS_rxReportError_((l_rx.var.poke.fill != (uint8_t)0)
? (uint8_t)QS_RX_FILL
: (uint8_t)QS_RX_POKE);
QS_RX_TRAN_(ERROR_STATE);
}
break;
}
case WAIT4_FILL_DATA: {
l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx);
l_rx.var.poke.idx += (uint8_t)8;
if ((l_rx.var.poke.idx >> 3) == l_rx.var.poke.size) {
QS_RX_TRAN_(WAIT4_FILL_FRAME);
}
break;
}
case WAIT4_POKE_DATA: {
l_rx.var.poke.data |= ((uint32_t)b << l_rx.var.poke.idx);
l_rx.var.poke.idx += (uint8_t)8;
if ((l_rx.var.poke.idx >> 3) == l_rx.var.poke.size) {
QS_rxPoke_();
--l_rx.var.poke.num;
if (l_rx.var.poke.num == (uint8_t)0) {
QS_RX_TRAN_(WAIT4_POKE_FRAME);
}
}
break;
}
case 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 WAIT4_GLB_FILTER_LEN: {
if (b == (uint8_t)sizeof(l_rx.var.gFlt.data)) {
l_rx.var.gFlt.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_GLB_FILTER_DATA);
}
else {
QS_rxReportError_((uint8_t)QS_RX_GLB_FILTER);
QS_RX_TRAN_(ERROR_STATE);
}
break;
}
case WAIT4_GLB_FILTER_DATA: {
l_rx.var.gFlt.data[l_rx.var.gFlt.idx] = b;
++l_rx.var.gFlt.idx;
if (l_rx.var.gFlt.idx == (uint8_t)sizeof(l_rx.var.gFlt.data)) {
QS_RX_TRAN_(WAIT4_GLB_FILTER_FRAME);
}
break;
}
case WAIT4_GLB_FILTER_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_OBJ_KIND: {
if (b <= (uint8_t)6) {
l_rx.var.obj.kind = b;
l_rx.var.obj.addr = (QSObj)0;
l_rx.var.obj.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_OBJ_ADDR);
}
else {
QS_rxReportError_(l_rx.var.obj.recId);
QS_RX_TRAN_(ERROR_STATE);
}
break;
}
case WAIT4_OBJ_ADDR: {
l_rx.var.obj.addr |= ((uint32_t)b << l_rx.var.obj.idx);
l_rx.var.obj.idx += (uint8_t)8;
if (l_rx.var.obj.idx == (uint8_t)(8*QS_OBJ_PTR_SIZE)) {
QS_RX_TRAN_(WAIT4_OBJ_FRAME);
}
break;
}
case WAIT4_OBJ_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_AO_FILTER_PRIO: {
l_rx.var.aFlt.prio = b;
QS_RX_TRAN_(WAIT4_AO_FILTER_FRAME);
break;
}
case WAIT4_AO_FILTER_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_EVT_PRIO: {
l_rx.var.evt.prio = b;
l_rx.var.evt.sig = (QSignal)0;
l_rx.var.evt.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_EVT_SIG);
break;
}
case WAIT4_EVT_SIG: {
l_rx.var.evt.sig |= (QSignal)((uint32_t)b << l_rx.var.evt.idx);
l_rx.var.evt.idx += (uint8_t)8;
if (l_rx.var.evt.idx == (uint8_t)(8*Q_SIGNAL_SIZE)) {
l_rx.var.evt.len = (uint16_t)0;
l_rx.var.evt.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_EVT_LEN);
}
break;
}
case WAIT4_EVT_LEN: {
l_rx.var.evt.len |= (uint16_t)((uint32_t)b << l_rx.var.evt.idx);
l_rx.var.evt.idx += (uint8_t)8;
if (l_rx.var.evt.idx == (uint8_t)(8*2)) {
if ((l_rx.var.evt.len + (uint16_t)sizeof(QEvt)) <=
(uint16_t)QF_poolGetMaxBlockSize())
{
/* report Ack before generating any other QS records */
QS_rxReportAck_(QS_RX_EVENT);
l_rx.var.evt.e = QF_newX_(
((uint_fast16_t)l_rx.var.evt.len
+ (uint_fast16_t)sizeof(QEvt)),
(uint_fast16_t)1, /* margin */
(enum_t)l_rx.var.evt.sig);
if (l_rx.var.evt.e != (QEvt *)0) { /* event allocated? */
l_rx.var.evt.p = (uint8_t *)l_rx.var.evt.e;
l_rx.var.evt.p += sizeof(QEvt);
if (l_rx.var.evt.len > (uint16_t)0) {
QS_RX_TRAN_(WAIT4_EVT_PAR);
}
else {
QS_RX_TRAN_(WAIT4_EVT_FRAME);
}
}
else {
QS_rxReportError_((uint8_t)QS_RX_EVENT);
QS_RX_TRAN_(ERROR_STATE);
}
}
else {
QS_rxReportError_((uint8_t)QS_RX_EVENT);
QS_RX_TRAN_(ERROR_STATE);
}
}
break;
}
case 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 == (uint16_t)0) {
QS_RX_TRAN_(WAIT4_EVT_FRAME);
}
break;
}
case WAIT4_EVT_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
#ifdef Q_UTEST
case WAIT4_TEST_SETUP_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_TEST_TEARDOWN_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_TEST_CONTINUE_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
case WAIT4_TEST_PROBE_DATA: {
l_rx.var.tp.data |= ((uint32_t)b << l_rx.var.tp.idx);
l_rx.var.tp.idx += (uint8_t)8;
if (l_rx.var.tp.idx == (uint8_t)(8*sizeof(uint32_t))) {
l_rx.var.tp.addr = (uint32_t)0;
l_rx.var.tp.idx = (uint8_t)0;
QS_RX_TRAN_(WAIT4_TEST_PROBE_ADDR);
}
break;
}
case WAIT4_TEST_PROBE_ADDR: {
l_rx.var.tp.addr |= ((uint32_t)b << l_rx.var.tp.idx);
l_rx.var.tp.idx += (uint8_t)8;
if (l_rx.var.tp.idx == (uint8_t)(8*QS_FUN_PTR_SIZE)) {
QS_RX_TRAN_(WAIT4_TEST_PROBE_FRAME);
}
break;
}
case WAIT4_TEST_PROBE_FRAME: {
/* keep ignoring the data until a frame is collected */
break;
}
#endif /* Q_UTEST */
case ERROR_STATE: {
/* keep ignoring the data until a good frame is collected */
break;
}
default: { /* unexpected or unimplemented state */
QS_rxReportError_((uint8_t)0x45);
QS_RX_TRAN_(ERROR_STATE);
break;
}
}
}
/****************************************************************************/
static void QS_rxHandleGoodFrame_(uint8_t state) {
uint8_t i;
uint8_t *ptr;
switch (state) {
case WAIT4_INFO_FRAME: {
/* no need to report Ack or Done */
QS_target_info_((uint8_t)0); /* 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_(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);
QS_rxReportDone_(QS_RX_COMMAND);
break;
}
case WAIT4_TICK_FRAME: {
QS_rxReportAck_(QS_RX_TICK);
QF_tickX_((uint_fast8_t)l_rx.var.tick.rate, &l_QS_RX);
QS_rxReportDone_(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]
+ l_rx.var.peek.offs);
QS_TIME_(); /* timestamp */
QS_U16_(l_rx.var.peek.offs); /* data offset */
QS_U8_(l_rx.var.peek.size); /* data size */
QS_U8_(l_rx.var.peek.num); /* number of data items */
for (i = (uint8_t)0; i < l_rx.var.peek.num; ++i) {
switch (l_rx.var.peek.size) {
case 1:
QS_U8_(*(ptr + i));
break;
case 2:
QS_U16_(*((uint16_t *)ptr + i));
break;
case 4:
QS_U32_(*((uint32_t *)ptr + i));
break;
default:
break;
}
}
QS_endRec();
QS_REC_DONE();
break;
}
case WAIT4_POKE_DATA: {
/* received less than expected poke data items */
QS_rxReportError_((uint8_t)QS_RX_POKE);
break;
}
case WAIT4_POKE_FRAME: {
QS_rxReportAck_(QS_RX_POKE);
/* no need to report done */
break;
}
case WAIT4_FILL_FRAME: {
QS_rxReportAck_(QS_RX_FILL);
ptr = ((uint8_t *)QS_rxPriv_.currObj[AP_OBJ]
+ l_rx.var.poke.offs);
for (i = (uint8_t)0; 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:
break;
}
}
break;
}
case WAIT4_GLB_FILTER_FRAME: {
QS_rxReportAck_(QS_RX_GLB_FILTER);
/* never disable the non-maskable records */
l_rx.var.gFlt.data[0] |= (uint8_t)0x01;
l_rx.var.gFlt.data[7] |= (uint8_t)0xFC;
l_rx.var.gFlt.data[8] |= (uint8_t)0x3F;
/* never disable the last 3 records on (0x7D, 0x7E, 0x7F) */
l_rx.var.gFlt.data[15] &= (uint8_t)0xE0;
for (i=(uint8_t)0; i < (uint8_t)sizeof(QS_priv_.glbFilter); ++i) {
QS_priv_.glbFilter[i] = l_rx.var.gFlt.data[i];
}
/* no need to report Done */
break;
}
case WAIT4_OBJ_FRAME: {
if (l_rx.var.obj.kind < (uint8_t)MAX_OBJ) {
if (l_rx.var.obj.recId == (uint8_t)QS_RX_LOC_FILTER) {
QS_priv_.locFilter[l_rx.var.obj.kind]
= (void *)l_rx.var.obj.addr;
}
else {
QS_rxPriv_.currObj[l_rx.var.obj.kind]
= (void *)l_rx.var.obj.addr;
}
QS_rxReportAck_((enum QSpyRxRecords)l_rx.var.obj.recId);
}
else if (l_rx.var.obj.kind == (uint8_t)6) { /* both SM and AO */
if (l_rx.var.obj.recId == (uint8_t)QS_RX_LOC_FILTER) {
QS_priv_.locFilter[SM_OBJ] = (void *)l_rx.var.obj.addr;
QS_priv_.locFilter[AO_OBJ] = (void *)l_rx.var.obj.addr;
}
else {
QS_rxPriv_.currObj[SM_OBJ] = (void *)l_rx.var.obj.addr;
QS_rxPriv_.currObj[AO_OBJ] = (void *)l_rx.var.obj.addr;
}
QS_rxReportAck_((enum QSpyRxRecords)l_rx.var.obj.recId);
}
else {
QS_rxReportError_(l_rx.var.obj.recId);
}
break;
}
case WAIT4_AO_FILTER_FRAME: {
QS_rxReportAck_(QS_RX_AO_FILTER);
if (l_rx.var.aFlt.prio <= (uint8_t)QF_MAX_ACTIVE) {
QS_rxReportAck_(QS_RX_AO_FILTER);
QS_priv_.locFilter[AO_OBJ] = QF_active_[l_rx.var.aFlt.prio];
QS_priv_.locFilter[SM_OBJ] = QF_active_[l_rx.var.aFlt.prio];
}
else {
QS_rxReportError_((uint8_t)QS_RX_AO_FILTER);
}
/* no need to report Done */
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); /* "massage" the event, if needed */
#endif /* Q_UTEST */
i = (uint8_t)0; /* use 'i' as status, 0 == success,no-recycle */
if (l_rx.var.evt.prio == (uint8_t)0) { /* publish */
QF_PUBLISH(l_rx.var.evt.e, &l_QS_RX);
QS_rxReportDone_(QS_RX_EVENT);
}
else if (l_rx.var.evt.prio < (uint8_t)QF_MAX_ACTIVE) {
if (QACTIVE_POST_X(QF_active_[l_rx.var.evt.prio],
l_rx.var.evt.e,
(uint_fast16_t)1, /* margin */
&l_QS_RX) == false)
{
/* failed QACTIVE_POST() recycles the event */
i = (uint8_t)0x80; /* failure status, no recycle */
}
}
else if (l_rx.var.evt.prio == (uint8_t)255) { /* special value */
/* dispatch to the current SM object */
if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) {
QHSM_DISPATCH((QHsm *)QS_rxPriv_.currObj[SM_OBJ],
l_rx.var.evt.e);
i = (uint8_t)0x01; /* success status, recycle needed */
}
else {
i = (uint8_t)0x81; /* failure status, recycle needed */
}
}
else if (l_rx.var.evt.prio == (uint8_t)254) { /* special value */
/* init the current SM object" */
if (QS_rxPriv_.currObj[SM_OBJ] != (void *)0) {
QHSM_INIT((QHsm *)QS_rxPriv_.currObj[SM_OBJ],
l_rx.var.evt.e);
i = (uint8_t)0x01; /* success status, recycle needed */
}
else {
i = (uint8_t)0x81; /* failure status, recycle needed */
}
}
else if (l_rx.var.evt.prio == (uint8_t)253) { /* special value */
/* 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,
(uint_fast16_t)1, /* margin */
&l_QS_RX) == false)
{
/* failed QACTIVE_POST() recycles the event */
i = (uint8_t)0x80; /* failure status, no recycle */
}
}
else {
i = (uint8_t)0x81; /* failure status, recycle needed */
}
}
else {
i = (uint8_t)0x81; /* failure status, recycle needed */
}
if ((i & (uint8_t)0x01) != (uint8_t)0) { /* recycle needed? */
QF_gc(l_rx.var.evt.e);
}
if ((i & (uint8_t)0x80) != (uint8_t)0) { /* failure? */
QS_rxReportError_((uint8_t)QS_RX_EVENT);
}
else {
QS_rxReportDone_(QS_RX_EVENT);
}
break;
}
#ifdef Q_UTEST
case WAIT4_TEST_SETUP_FRAME: {
QS_rxReportAck_(QS_RX_TEST_SETUP);
l_testData.tpNum = (uint8_t)0; /* clear the Test-Probes */
l_testData.testTime = (QSTimeCtr)0; /* 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_(QS_RX_TEST_TEARDOWN);
QS_onTestTeardown(); /* application-specific test teardown */
/* no need to report Done */
break;
}
case WAIT4_TEST_CONTINUE_FRAME: {
QS_rxReportAck_(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_(QS_RX_TEST_PROBE);
Q_ASSERT_ID(815, l_testData.tpNum
< (uint8_t)(sizeof(l_testData.tpBuf)
/sizeof(l_testData.tpBuf[0])));
l_testData.tpBuf[l_testData.tpNum] = l_rx.var.tp;
++l_testData.tpNum;
/* no need to report Done */
break;
}
#endif /* Q_UTEST */
case ERROR_STATE: {
/* keep ignoring all bytes until new frame */
break;
}
default: {
QS_rxReportError_((uint8_t)0x47);
break;
}
}
}
/****************************************************************************/
static void QS_rxHandleBadFrame_(uint8_t state) {
QS_rxReportError_((uint8_t)0x50); /* report error for all bad frames */
switch (state) {
case WAIT4_EVT_FRAME: {
Q_ASSERT_ID(910, l_rx.var.evt.e != (QEvt *)0);
QF_gc(l_rx.var.evt.e); /* don't leak an allocated event */
break;
}
default: {
break;
}
}
}
/****************************************************************************/
static void QS_rxReportAck_(enum QSpyRxRecords recId) {
QS_beginRec((uint_fast8_t)QS_RX_STATUS);
QS_U8_(recId); /* record ID */
QS_endRec();
QS_REC_DONE();
}
/****************************************************************************/
static void QS_rxReportError_(uint8_t code) {
QS_beginRec((uint_fast8_t)QS_RX_STATUS);
QS_U8_((uint8_t)(0x80U | code)); /* error code */
QS_endRec();
QS_REC_DONE();
}
/****************************************************************************/
static void QS_rxReportDone_(enum QSpyRxRecords recId) {
QS_beginRec((uint_fast8_t)QS_TARGET_DONE);
QS_TIME_(); /* timestamp */
QS_U8_(recId); /* record ID */
QS_endRec();
QS_REC_DONE();
}
/****************************************************************************/
static void QS_rxPoke_(void) {
uint8_t *ptr = ((uint8_t *)QS_rxPriv_.currObj[AP_OBJ]
+ 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_ID(900);
break;
}
l_rx.var.poke.data = (uint32_t)0;
l_rx.var.poke.idx = (uint8_t)0;
l_rx.var.poke.offs += l_rx.var.poke.size;
}
/*==========================================================================*/
#ifdef Q_UTEST
/****************************************************************************/
/**
* @description
* This function obtains the Test-Probe for a given API.
*
* @param[in] api_id the API-ID that requests its Test-Probe
*
* @returs Test-Probe data that has been received for the given API
* from the Host (running qutest). For any ginve API, the function returns
* the Test-Probe data in the same order as it was received from the Host.
* If there is no Test-Probe for a ginve API, or no more Test-Probes for
* a given API, the function returns zero.
*/
uint32_t QS_getTestProbe_(void (* const api)(void)) {
uint32_t data = (uint32_t)0;
uint8_t i;
for (i = (uint8_t)0; i < l_testData.tpNum; ++i) {
if (l_testData.tpBuf[i].addr == (QSFun)api) {
data = l_testData.tpBuf[i].data;
--l_testData.tpNum;
/* move all remaining entries in the buffer up by one */
for (; i < l_testData.tpNum; ++i) {
l_testData.tpBuf[i] = l_testData.tpBuf[i + 1];
}
/* i == l_testData.tpNum, which terminates the for loop */
QS_beginRec((uint_fast8_t)QS_TEST_PROBE_GET);
QS_TIME_(); /* timestamp */
QS_FUN_(api); /* the calling API */
QS_U32_(data); /* the Test-Probe data */
QS_endRec();
QS_REC_DONE();
}
}
return data;
}
/****************************************************************************/
QSTimeCtr QS_onGetTime(void) {
return (++l_testData.testTime);
}
#endif /* Q_UTEST */