forked from 0xha0/df_datalink
1063 lines
26 KiB
C
1063 lines
26 KiB
C
/*****************************************************************************/
|
|
/**
|
|
* @file systembus.c
|
|
* @author FanHao ([email protected])
|
|
* @brief System bus protocol implementation file.
|
|
* @version 0.1
|
|
* @date 2023-05-06
|
|
*
|
|
* @copyright Copyright(c) 2024 Beijing Institute of Technology.\n
|
|
* Lab of Communication and Network
|
|
*
|
|
******************************************************************************/
|
|
|
|
/**
|
|
* @addtogroup SystemBus
|
|
* @{
|
|
*/
|
|
|
|
/***************************** Include Files *********************************/
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdint.h>
|
|
#include <stdbool.h>
|
|
#include "systembus_uart.h"
|
|
#include "systembus.h"
|
|
#include "util.h"
|
|
#include "common.h"
|
|
#include "error.h"
|
|
#include "circ_queue.h"
|
|
// TODO: better approach?
|
|
#include "../networking/link_layer/link_layer.h"
|
|
#include "../networking/phy_layer/phy_layer.h"
|
|
|
|
/**
|
|
* @addtogroup SystemBus_Protocol_Routine
|
|
* @{
|
|
*/
|
|
|
|
/************************** Constant Definitions *****************************/
|
|
/**
|
|
* @name SystemBus_MSG_Constants
|
|
* @{
|
|
*/
|
|
// Common Field
|
|
// -- Command word
|
|
#define WD_CMD 0x00
|
|
|
|
// -- Production type
|
|
#define WD_PRD 0x01
|
|
#define PRD_DL_K 0xDD
|
|
#define PRD_DL_C 0xEE
|
|
#define PRD_ALL 0xFF
|
|
|
|
// Self-Test
|
|
#define CMD_SELF_TEST 0xE1
|
|
// -- Request
|
|
// -- -- --
|
|
// -- Response
|
|
#define ST_RSP_LEN 23
|
|
// -- -- Self-Test Result
|
|
#define WD_ST_RET 0x02
|
|
#define ST_RET_OK 0xAA
|
|
#define ST_RET_ERROR 0x55
|
|
// -- -- Hardware info offset
|
|
#define SP_INFO_OFFSET 0x03
|
|
#define ST_INFO_LEN 20
|
|
|
|
// Data Echo Test Command
|
|
#define CMD_ECHO 0xE2
|
|
|
|
// Configuration
|
|
#define CMD_CONFIG 0xE3
|
|
// -- Request
|
|
// -- -- Enable control
|
|
#define WD_EN_CMD 0x02
|
|
#define EN_CMD_ENABLE 0xAA
|
|
#define EN_CMD_SILENT 0x55
|
|
// -- -- Node mode control
|
|
#define WD_ID_CMD 0x03
|
|
// -- -- Service Rate control
|
|
#define WD_RATE_CMD 0x04
|
|
#define RATE_CMD_128K 0x22
|
|
#define RATE_CMD_512K 0x33
|
|
#define RATE_CMD_2M 0x44
|
|
// -- -- Routing table select
|
|
#define WD_RTNE_CMD 0x05
|
|
// -- Response
|
|
// -- -- none response
|
|
|
|
// System info update
|
|
#define CMD_SYS_INFO 0x33
|
|
// -- Request
|
|
// -- -- 54 byte system info
|
|
#define SYS_INFO_OFFSET 0x02
|
|
// -- Response
|
|
// -- -- none response
|
|
|
|
// Data Send Command
|
|
#define CMD_SEND_DATA 0xE4
|
|
// -- Request
|
|
// -- -- destination ID
|
|
#define SEND_DATA_ID_OFFSET 0x02
|
|
#define SEND_DATA_ID_LEN 4
|
|
#define WD_DST_ID 0x06
|
|
// -- -- data length
|
|
#define SEND_DATA_LEN_OFFSET 0x07
|
|
#define SEND_DATA_LEN_LEN 2
|
|
// -- -- <= 1000 byte payload
|
|
#define SEND_DATA_OFFSET 0x09
|
|
// -- Response
|
|
// -- -- none response
|
|
|
|
// Data Receive Command
|
|
#define CMD_RECV_DATA 0xE5
|
|
// -- Request
|
|
// -- -- --
|
|
// -- Response
|
|
// -- -- source ID
|
|
#define RECV_DATA_ID_OFFSET 0x02
|
|
#define RECV_DATA_ID_LEN 4
|
|
#define WD_SRC_ID 0x06
|
|
// -- -- data length
|
|
#define RECV_DATA_LEN_OFFSET 0x07
|
|
#define RECV_DATA_LEN_LEN 2
|
|
// -- -- <= 1000 byte payload
|
|
#define RECV_DATA_OFFSET 0x09
|
|
|
|
// Remote Test
|
|
#define CMD_RM_TEST 0xE6
|
|
// -- Request
|
|
// -- -- --
|
|
// -- Response
|
|
#define RM_TEST_RSP_LEN 202
|
|
// -- -- 8 byte time stamp
|
|
#define RT_TIME_STAMP_OFFSET 0x02
|
|
#define RT_TIME_STAMP_LEN 8
|
|
// -- -- 16 byte system bus log
|
|
#define RT_SYSBUS_LOG_OFFSET 0x0A
|
|
#define RT_SYSBUS_LOG_LEN 16
|
|
// -- -- 88 byte link layer log
|
|
#define RT_LINK_LOG_OFFSET 0x1A
|
|
#define RT_LINK_LOG_LEN 100
|
|
// -- -- 56 byte physical layer log
|
|
#define RT_PHY_LOG_OFFSET 0x7E
|
|
#define RT_PHY_LOG_LEN 56
|
|
// -- -- 20 byte hardware log
|
|
#define RT_HARDWARE_ST_OFFSET 0xB6
|
|
#define RT_HARDWARE_ST_LEN 20
|
|
|
|
// My Production Type
|
|
#define MY_PRD_ID PRD_DL_C
|
|
/** @} SystemBus_MSG_Constants*/
|
|
#define BUFF_A 0x00
|
|
#define BUFF_B 0x01
|
|
#define BUFF_NONE 0xFF
|
|
|
|
#define SYSINFO_PACK_LEN 54 // bytes
|
|
#define PAYLOAD_PACK_LEN 46 // bytes
|
|
#define SLOTLOG_PACK_LEN 88 // bytes
|
|
#define PRTCLLOG_PACK_ELN 24 // bytes
|
|
#define HWSTATE_PACK_ELN 12 // bytes
|
|
/************************** Data Type Definitions ****************************/
|
|
/**
|
|
* @struct PINGPONG
|
|
* @brief PingPing Control
|
|
*/
|
|
typedef struct
|
|
{
|
|
uint8_t latest; ///< latest available index
|
|
uint8_t rBusy; ///< current read busy index
|
|
uint8_t wBusy; ///< current write busy index
|
|
} PINGPONG;
|
|
|
|
/**
|
|
* @struct SYSMONITOR
|
|
* @brief System Monitor struct holds all the system log info
|
|
*/
|
|
typedef struct
|
|
{
|
|
SYSMON_CONFIG config; ///< system monitor initialization parameters
|
|
CIRC_QUEUE *sendQueue[L2_NET_SCALE]; ///< send queue
|
|
CIRC_QUEUE *recvQueue[L2_NET_SCALE]; ///< receive queue
|
|
PINGPONG sysInfoCtrl;
|
|
SYSINFO sysInfo[2];
|
|
PINGPONG linkLogCtrl;
|
|
LINKLOG linkLog[2];
|
|
PINGPONG phyLogCtrl;
|
|
PHYLOG phyLog[2];
|
|
PINGPONG hwStateCtrl;
|
|
HWSTATE hwState[2];
|
|
} SYSMONITOR;
|
|
/************************** Function Prototypes ******************************/
|
|
static uint8_t GetWritable(PINGPONG ctrllor);
|
|
static uint8_t GetReadable(PINGPONG ctrllor);
|
|
static int32_t SystemBus_GenSelfTestRsp(SYSBUS *instPtr);
|
|
static int32_t SystemBus_SystemConfig(SYSBUS *instPtr, uint8_t *msg);
|
|
static int32_t SystemBus_Echo(SYSBUS *instPtr, SYSBUS_UART_FRAME *frame);
|
|
static int32_t SystemBus_GenRemoteTestRsp(SYSBUS *instPtr, uint8_t *msg);
|
|
/**************************** Type Definitions *******************************/
|
|
/************************** Variable Definitions *****************************/
|
|
// TODO: better approach?
|
|
extern L2_INST *linkInstPtr;
|
|
extern L2_CONFIG linkConfig;
|
|
extern SYSBUS *sysbusDescPtr;
|
|
static SYSMONITOR *sysMonitorInst;
|
|
|
|
inline static uint8_t GetWritable(PINGPONG ctrllor)
|
|
{
|
|
// Allow only one write.
|
|
ASSERT(ctrllor.wBusy == BUFF_NONE);
|
|
if (ctrllor.rBusy == BUFF_NONE)
|
|
return ((ctrllor.latest == BUFF_B) ? BUFF_A : BUFF_B);
|
|
else
|
|
return ((ctrllor.rBusy == BUFF_B) ? BUFF_A : BUFF_B);
|
|
}
|
|
|
|
inline static uint8_t GetReadable(PINGPONG ctrllor)
|
|
{
|
|
if (ctrllor.wBusy == BUFF_NONE || ctrllor.wBusy != ctrllor.latest)
|
|
return ((ctrllor.latest == BUFF_NONE) ? BUFF_A : BUFF_B);
|
|
else
|
|
return ((ctrllor.wBusy == BUFF_B) ? BUFF_A : BUFF_B);
|
|
}
|
|
|
|
inline bool SystemBus_IsReqPending(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!isCircQueueEmpty(instPtr->rxQueue));
|
|
}
|
|
|
|
inline bool SystemBus_IsRespPending(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!isCircQueueEmpty(instPtr->txQueue));
|
|
}
|
|
|
|
inline bool SystemBus_IsTxBusy(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return (!instPtr->isTxDone);
|
|
}
|
|
|
|
inline bool SystemBus_IsRxBusy(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
return SysbusUart_IsRxBusy(instPtr->uartInst);
|
|
}
|
|
|
|
/**
|
|
* @brief Generate System Bus SelfTest response.
|
|
*/
|
|
static int32_t SystemBus_GenSelfTestRsp(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
SYSBUS_UART_FRAME *sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME));
|
|
if (sysbusRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for SELF TEST Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
HWSTATE *hwStatsPtr = &(instPtr->hwState);
|
|
sysbusRsp->len = ST_RSP_LEN;
|
|
memset(sysbusRsp->data, 0x00, ST_RSP_LEN);
|
|
sysbusRsp->data[WD_CMD] = CMD_SELF_TEST;
|
|
sysbusRsp->data[WD_PRD] = MY_PRD_ID;
|
|
SysMonitor_GetHardwareStatus(hwStatsPtr);
|
|
if (L2_isStarted(linkInstPtr))
|
|
{
|
|
sysbusRsp->data[WD_ST_RET] = (hwStatsPtr->isSpOk && hwStatsPtr->isAntOk) ? ST_RET_OK : ST_RET_ERROR;
|
|
}
|
|
else
|
|
{
|
|
sysbusRsp->data[WD_ST_RET] = (hwStatsPtr->isSpOk) ? ST_RET_OK : ST_RET_ERROR;
|
|
}
|
|
// apply status and omit 2 bytes of overhead.
|
|
memcpy(sysbusRsp->data + SP_INFO_OFFSET, &(hwStatsPtr->spFpgaVolt), ST_INFO_LEN);
|
|
|
|
if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false)
|
|
{
|
|
free(sysbusRsp);
|
|
DBG_ERROR("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Apply system configuration
|
|
*/
|
|
static int32_t SystemBus_SystemConfig(SYSBUS *instPtr, uint8_t *msg)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(msg != NULL);
|
|
PHY_INST *phyRef = linkInstPtr->config.phyInstPtr;
|
|
TMC_INST *tmcRef = linkInstPtr->config.tmcInstPtr;
|
|
bool reconfig = false;
|
|
|
|
// Node mode control
|
|
if (msg[WD_ID_CMD] >= 0 && msg[WD_ID_CMD] < linkConfig.netScale)
|
|
{
|
|
reconfig |= (msg[WD_ID_CMD] != linkConfig.myId);
|
|
linkConfig.myType = (msg[WD_ID_CMD] == L2_HEAD_ID) ? L2_NODE_HEAD : L2_NODE_MEMBER;
|
|
linkConfig.myId = msg[WD_ID_CMD];
|
|
}
|
|
else
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_ID_CMD, msg[WD_ID_CMD]);
|
|
return EPARAM;
|
|
}
|
|
|
|
// Service Rate control
|
|
switch (msg[WD_RATE_CMD])
|
|
{
|
|
case RATE_CMD_128K:
|
|
{
|
|
reconfig |= (linkConfig.serviceRate != L2_SVC_128K);
|
|
linkConfig.serviceRate = L2_SVC_128K;
|
|
break;
|
|
}
|
|
case RATE_CMD_512K:
|
|
{
|
|
reconfig |= (linkConfig.serviceRate != L2_SVC_512K);
|
|
linkConfig.serviceRate = L2_SVC_512K;
|
|
break;
|
|
}
|
|
case RATE_CMD_2M:
|
|
{
|
|
reconfig |= (linkConfig.serviceRate != L2_SVC_2M);
|
|
linkConfig.serviceRate = L2_SVC_2M;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_RATE_CMD, msg[WD_RATE_CMD]);
|
|
return EPARAM;
|
|
}
|
|
}
|
|
|
|
// Routing table selection
|
|
if (msg[WD_RTNE_CMD] != instPtr->sysbusLog.lastConfig[3])
|
|
{
|
|
switch (msg[WD_RTNE_CMD])
|
|
{
|
|
case 1:
|
|
{
|
|
reconfig |= (linkConfig.initTable != normalRoutTable1);
|
|
reconfig |= (linkConfig.normalTable != normalRoutTable1);
|
|
linkConfig.initTable = normalRoutTable1;
|
|
linkConfig.normalTable = normalRoutTable1;
|
|
break;
|
|
}
|
|
case 2:
|
|
{
|
|
reconfig |= (linkConfig.initTable != normalRoutTable2);
|
|
reconfig |= (linkConfig.normalTable != normalRoutTable2);
|
|
linkConfig.initTable = normalRoutTable2;
|
|
linkConfig.normalTable = normalRoutTable2;
|
|
break;
|
|
}
|
|
case 3:
|
|
{
|
|
reconfig |= (linkConfig.initTable != normalRoutTable3);
|
|
reconfig |= (linkConfig.normalTable != normalRoutTable3);
|
|
linkConfig.initTable = normalRoutTable3;
|
|
linkConfig.normalTable = normalRoutTable3;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_RTNE_CMD, msg[WD_RTNE_CMD]);
|
|
return EIGNORE;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Enable control
|
|
if (msg[WD_EN_CMD] == EN_CMD_ENABLE)
|
|
{
|
|
if (reconfig || !L2_isStarted(linkInstPtr))
|
|
{
|
|
Linklayer_Reconfig(linkInstPtr, &linkConfig);
|
|
}
|
|
Linklayer_Start(linkInstPtr);
|
|
}
|
|
else if (msg[WD_EN_CMD] == EN_CMD_SILENT)
|
|
{
|
|
Linklayer_Halt(linkInstPtr);
|
|
// configure phy to generate remote test
|
|
TIME_WORD pullTime = {0, 0};
|
|
pullTime.int_part = TimeCtrl_GetIntCnt(tmcRef) + 1;
|
|
if (Phy_SetTimeWord(phyRef, &pullTime, PHY_TIME_WORD_POLL) != SUCCESS)
|
|
{
|
|
DBG_ERROR("Physical set pull time failed.\n");
|
|
}
|
|
Phy_SetupNextSlot(phyRef, PHY_TOR_RX, L1_FRAME_ADV_24MS, NULL);
|
|
}
|
|
else
|
|
{
|
|
// Ignore current command
|
|
DBG_ERROR("Unexpected config command 0x%02X:0x%02X",
|
|
WD_EN_CMD, msg[WD_EN_CMD]);
|
|
return EIGNORE;
|
|
}
|
|
|
|
instPtr->sysbusLog.lastConfig[0] = msg[WD_EN_CMD];
|
|
instPtr->sysbusLog.lastConfig[1] = msg[WD_ID_CMD];
|
|
instPtr->sysbusLog.lastConfig[2] = msg[WD_RATE_CMD];
|
|
instPtr->sysbusLog.lastConfig[3] = msg[WD_RTNE_CMD];
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Echo System Bus frame
|
|
*/
|
|
static int32_t SystemBus_Echo(SYSBUS *instPtr, SYSBUS_UART_FRAME *frame)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(frame != NULL);
|
|
SYSBUS_UART_FRAME *sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME));
|
|
if (sysbusRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for ECHO TEST Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
sysbusRsp->len = frame->len;
|
|
memcpy(sysbusRsp->data, frame->data, sysbusRsp->len);
|
|
if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false)
|
|
{
|
|
free(sysbusRsp);
|
|
DBG_ERROR("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Generate System Bus receive call command response
|
|
*/
|
|
int32_t SystemBus_GenRecvRsp(SYSBUS *instPtr, SYSBUS_SVC_PLD *payload)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(payload != NULL);
|
|
|
|
SYSBUS_UART_FRAME *sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME));
|
|
if (sysbusRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for Receive Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
|
|
sysbusRsp->len = payload->dataLen + RECV_DATA_OFFSET;
|
|
|
|
sysbusRsp->data[WD_CMD] = CMD_RECV_DATA;
|
|
sysbusRsp->data[WD_PRD] = MY_PRD_ID;
|
|
memcpy(sysbusRsp->data + RECV_DATA_ID_OFFSET, payload, sizeof(SYSBUS_SVC_PLD));
|
|
|
|
if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false)
|
|
{
|
|
free(sysbusRsp);
|
|
DBG_WARNING("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
instPtr->sysbusLog.recvCnt++;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Generate System Bus remote test command response
|
|
*/
|
|
static int32_t SystemBus_GenRemoteTestRsp(SYSBUS *instPtr, uint8_t *msg)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(msg != NULL);
|
|
SYSBUS_UART_FRAME *sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME));
|
|
if (sysbusRsp == NULL)
|
|
{
|
|
DBG_ERROR("Failed to allocate memory for LJ REMOTE TEST Response!\n");
|
|
return ENOMEM;
|
|
}
|
|
|
|
TIME_WORD tmWord;
|
|
uint64_t time;
|
|
TimeCtrl_GetCurrentTime(linkInstPtr->config.tmcInstPtr, &tmWord);
|
|
time = (uint64_t)tmWord.int_part * TIME_FRAC_MAX + tmWord.frac_part;
|
|
|
|
if (msg[WD_CMD] == CMD_RM_TEST)
|
|
{
|
|
sysbusRsp->len = RM_TEST_RSP_LEN;
|
|
memset(sysbusRsp->data, 0x00, RM_TEST_RSP_LEN);
|
|
sysbusRsp->data[WD_CMD] = CMD_RM_TEST;
|
|
sysbusRsp->data[WD_PRD] = MY_PRD_ID;
|
|
|
|
memcpy(sysbusRsp->data + RT_TIME_STAMP_OFFSET, &time, RT_TIME_STAMP_LEN);
|
|
SysMonitor_GetLinklayerLog(&(instPtr->linkLog));
|
|
SysMonitor_GetPhysicalLayerLog(&(instPtr->phyLog));
|
|
SysMonitor_GetHardwareStatus(&(instPtr->hwState));
|
|
memcpy(sysbusRsp->data + RT_SYSBUS_LOG_OFFSET, &(instPtr->sysbusLog), RT_SYSBUS_LOG_LEN);
|
|
memcpy(sysbusRsp->data + RT_LINK_LOG_OFFSET, &(instPtr->linkLog), RT_LINK_LOG_LEN);
|
|
memcpy(sysbusRsp->data + RT_PHY_LOG_OFFSET, &(instPtr->phyLog), RT_PHY_LOG_LEN);
|
|
memcpy(sysbusRsp->data + RT_HARDWARE_ST_OFFSET, &(instPtr->hwState.spFpgaVolt), RT_HARDWARE_ST_LEN);
|
|
}
|
|
else
|
|
{
|
|
DBG_ERROR("Unexpected command 0x%02X.\n", msg[WD_CMD]);
|
|
free(sysbusRsp);
|
|
return EUNEXP;
|
|
}
|
|
|
|
if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false)
|
|
{
|
|
free(sysbusRsp);
|
|
DBG_WARNING("SystemBus::TX Queue overflowed!\n");
|
|
return EQUEUE;
|
|
}
|
|
instPtr->sysbusLog.remoteCnt++;
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief A handler function. This handler evaluate the message from system
|
|
* bus receive queue, and make appropriate response.
|
|
*
|
|
* @param instPtr A pointer to System Bus instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t SystemBus_HandleCmd(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
int32_t Status;
|
|
SYSBUS_UART_FRAME *sysbusReq;
|
|
uint8_t *msg;
|
|
if (CircQueuePop(instPtr->rxQueue, (void **)(&sysbusReq)) == false)
|
|
{
|
|
DBG_WARNING("SystemBus::Nothing to be handled.\n");
|
|
return EIGNORE;
|
|
}
|
|
msg = sysbusReq->data;
|
|
if (msg[WD_PRD] != MY_PRD_ID && msg[WD_PRD] != PRD_ALL)
|
|
{
|
|
DBG_WARNING("SystemBus::Production code mismatch!\n");
|
|
return EIGNORE;
|
|
}
|
|
|
|
switch (msg[WD_CMD])
|
|
{
|
|
case CMD_SELF_TEST:
|
|
{
|
|
Status = SystemBus_GenSelfTestRsp(instPtr);
|
|
break;
|
|
}
|
|
case CMD_ECHO:
|
|
{
|
|
Status = SystemBus_Echo(instPtr, sysbusReq);
|
|
break;
|
|
}
|
|
case CMD_CONFIG:
|
|
{
|
|
Status = SystemBus_SystemConfig(instPtr, msg);
|
|
break;
|
|
}
|
|
case CMD_SYS_INFO:
|
|
{
|
|
SysMonitor_UpdateSysinfo((SYSINFO *)(msg + SYS_INFO_OFFSET));
|
|
Status = SUCCESS;
|
|
break;
|
|
}
|
|
case CMD_SEND_DATA:
|
|
{
|
|
Status = SysMonitor_UpdateSendPayload((SYSBUS_SVC_PLD *)(msg + SEND_DATA_ID_OFFSET));
|
|
if (Status == SUCCESS)
|
|
{
|
|
instPtr->sysbusLog.sendCnt++;
|
|
}
|
|
break;
|
|
}
|
|
case CMD_RM_TEST:
|
|
{
|
|
Status = SystemBus_GenRemoteTestRsp(instPtr, msg);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
DBG_ERROR("SystemBus::Unknown command code!\n");
|
|
Status = FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
free(sysbusReq);
|
|
return Status;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function send a repose frame from system bus tx queue.
|
|
*
|
|
* @param instPtr A pointer to System Bus instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t SystemBus_MakeResp(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
int32_t Status;
|
|
SYSBUS_UART_FRAME *sysbusRsp;
|
|
if (CircQueuePop(instPtr->txQueue, (void **)(&sysbusRsp)) == false)
|
|
{
|
|
DBG_WARNING("SystemBus::Nothing to be sent.\n");
|
|
return EIGNORE;
|
|
}
|
|
instPtr->isTxDone = false; // A transmission procedure is started.
|
|
Status = SysbusUart_Send(instPtr->uartInst, sysbusRsp);
|
|
free(sysbusRsp);
|
|
return Status;
|
|
}
|
|
|
|
/** @} */
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief Initialize System Bus instance.
|
|
*
|
|
* @param InstPtr A double pointer to System Bus instance.
|
|
* @param config A pointer to System Bus configurations.
|
|
*
|
|
* @return Error code.
|
|
*
|
|
******************************************************************************/
|
|
int32_t SystemBus_Init(SYSBUS **instPtr, SYSBUS_CONFIG *config)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
ASSERT(config != NULL);
|
|
SYSBUS_UART_CONFIG uartConfig;
|
|
int32_t Status;
|
|
(*instPtr) = malloc(sizeof(SYSBUS));
|
|
if ((*instPtr) == NULL)
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
(*instPtr)->rxQueue = NULL;
|
|
(*instPtr)->txQueue = NULL;
|
|
(*instPtr)->uartInst = NULL;
|
|
memcpy(&((*instPtr)->config), config, sizeof(SYSBUS_CONFIG));
|
|
memset(&((*instPtr)->sysbusLog), 0x00, sizeof(SYSBUSLOG));
|
|
memset(&((*instPtr)->linkLog), 0x00, sizeof(LINKLOG));
|
|
memset(&((*instPtr)->phyLog), 0x00, sizeof(PHYLOG));
|
|
memset(&((*instPtr)->hwState), 0x00, sizeof(HWSTATE));
|
|
uartConfig.baseAddr = config->baseAddr;
|
|
Status = SysbusUart_Init(&((*instPtr)->uartInst), &uartConfig);
|
|
if (Status != SUCCESS)
|
|
{
|
|
SystemBus_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
if (CircQueueCreate(&((*instPtr)->txQueue), (*instPtr)->config.txQueueMax) == false)
|
|
{
|
|
SystemBus_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
if (CircQueueCreate(&((*instPtr)->rxQueue), (*instPtr)->config.rxQueueMax) == false)
|
|
{
|
|
SystemBus_Destroy(*instPtr);
|
|
return ENOMEM;
|
|
}
|
|
(*instPtr)->isRxDone = false; // nothing received.
|
|
(*instPtr)->isTxDone = true; // nothing to be sent.
|
|
return SUCCESS;
|
|
}
|
|
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief Destroy System Bus instance.
|
|
*
|
|
* @param InstPtr A pointer to System Bus instance.
|
|
*
|
|
* @return None.
|
|
*
|
|
******************************************************************************/
|
|
void SystemBus_Destroy(SYSBUS *instPtr)
|
|
{
|
|
if (instPtr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
free(instPtr->uartInst);
|
|
free(instPtr->txQueue);
|
|
free(instPtr->rxQueue);
|
|
free(instPtr);
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function handles the systembus tx irq events.
|
|
*
|
|
* @param instPtr A pointer to System Bus instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t SystemBus_TxIntrHandler(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
// nothing special...
|
|
instPtr->isTxDone = true; // A transmission procedure finished.
|
|
return SUCCESS;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/**
|
|
* @brief This function handles the systembus rx irq events.
|
|
*
|
|
* @param instPtr A pointer to System Bus instance.
|
|
*
|
|
* @return None
|
|
*
|
|
******************************************************************************/
|
|
int32_t SystemBus_RxIntrHandler(SYSBUS *instPtr)
|
|
{
|
|
ASSERT(instPtr != NULL);
|
|
instPtr->isRxDone = true; // whether reception successes or not, receiving procedure ended.
|
|
int32_t Status;
|
|
SYSBUS_UART_FRAME *sysbusReq = malloc(sizeof(SYSBUS_UART_FRAME));
|
|
if (sysbusReq == NULL)
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
Status = SysbusUart_Recv(instPtr->uartInst, sysbusReq);
|
|
if (Status != SUCCESS)
|
|
{
|
|
free(sysbusReq);
|
|
return Status;
|
|
}
|
|
if (CircQueuePush(instPtr->rxQueue, (void *)sysbusReq) == false)
|
|
{
|
|
DBG_ERROR("SystemBus::RX Queue overflowed!\n");
|
|
free(sysbusReq);
|
|
return EQUEUE;
|
|
}
|
|
instPtr->isRxDone = false; // another reception is undergoing.
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize System monitor
|
|
*
|
|
* @return SUCCESS
|
|
*/
|
|
int32_t SysMonitor_Init(SYSMON_CONFIG *config)
|
|
{
|
|
ASSERT(config != NULL);
|
|
|
|
sysMonitorInst = calloc(1, sizeof(SYSMONITOR));
|
|
if (sysMonitorInst == NULL)
|
|
{
|
|
return ENOMEM;
|
|
}
|
|
|
|
memcpy(&(sysMonitorInst->config), config, sizeof(SYSMON_CONFIG));
|
|
memset(&(sysMonitorInst->sysInfoCtrl), BUFF_NONE, sizeof(PINGPONG));
|
|
memset(&(sysMonitorInst->linkLogCtrl), BUFF_NONE, sizeof(PINGPONG));
|
|
memset(&(sysMonitorInst->phyLogCtrl), BUFF_NONE, sizeof(PINGPONG));
|
|
memset(&(sysMonitorInst->hwStateCtrl), BUFF_NONE, sizeof(PINGPONG));
|
|
|
|
for (uint8_t ii = 0; ii < L2_NET_SCALE; ii++)
|
|
{
|
|
sysMonitorInst->sendQueue[ii] = NULL;
|
|
sysMonitorInst->recvQueue[ii] = NULL;
|
|
if (CircQueueCreate(&(sysMonitorInst->sendQueue[ii]),
|
|
sysMonitorInst->config.sendQueueMax) == false)
|
|
{
|
|
SysMonitor_Destroy();
|
|
return ENOMEM;
|
|
}
|
|
if (CircQueueCreate(&(sysMonitorInst->recvQueue[ii]),
|
|
sysMonitorInst->config.recvQueueMax) == false)
|
|
{
|
|
SysMonitor_Destroy();
|
|
return ENOMEM;
|
|
}
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Destroy system monitor release all allocated memory
|
|
*/
|
|
void SysMonitor_Destroy(void)
|
|
{
|
|
free(sysMonitorInst);
|
|
}
|
|
|
|
/**
|
|
* @name SystemInfo_API
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Update system info.
|
|
*
|
|
* @param info
|
|
*/
|
|
void SysMonitor_UpdateSysinfo(const SYSINFO *info)
|
|
{
|
|
ASSERT(info != NULL);
|
|
uint8_t buffIndx;
|
|
buffIndx = GetWritable(sysMonitorInst->sysInfoCtrl);
|
|
sysMonitorInst->sysInfoCtrl.wBusy = buffIndx;
|
|
memcpy(&(sysMonitorInst->sysInfo[buffIndx]), info, sizeof(SYSINFO));
|
|
sysMonitorInst->sysInfoCtrl.latest = buffIndx;
|
|
sysMonitorInst->sysInfoCtrl.wBusy = BUFF_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get system info.
|
|
*
|
|
* @param info
|
|
*/
|
|
void SysMonitor_GetSysinfo(SYSINFO *info)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(info != NULL);
|
|
uint8_t buffIndx, buffIndex_old;
|
|
buffIndex_old = sysMonitorInst->sysInfoCtrl.rBusy;
|
|
if (buffIndex_old != BUFF_NONE)
|
|
buffIndx = buffIndex_old; // Keep it busy
|
|
else
|
|
buffIndx = GetReadable(sysMonitorInst->sysInfoCtrl);
|
|
sysMonitorInst->sysInfoCtrl.rBusy = buffIndx;
|
|
memcpy(info, &(sysMonitorInst->sysInfo[buffIndx]), sizeof(SYSINFO));
|
|
sysMonitorInst->sysInfoCtrl.rBusy = buffIndex_old;
|
|
}
|
|
/** @} SystemInfo_API*/
|
|
|
|
/**
|
|
* @name ServicePayload_API
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Update send payload by
|
|
*
|
|
* @param payload
|
|
*/
|
|
int32_t SysMonitor_UpdateSendPayload(SYSBUS_SVC_PLD *payload)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(payload != NULL);
|
|
|
|
// validity check
|
|
if (payload->dataLen > SYSBUS_PLD_MAX)
|
|
{
|
|
DBG_ERROR("Payload length %d exceeds valid range.", payload->dataLen);
|
|
return EPARAM;
|
|
}
|
|
if (payload->nodeId < L2_NODE_NONE || payload->nodeId > L2_NET_SCALE)
|
|
{
|
|
DBG_ERROR("Node ID %d exceeds valid range.", payload->nodeId);
|
|
return EPARAM;
|
|
}
|
|
|
|
// push message into send queue
|
|
SYSBUS_SVC_PLD *payloadCopy = malloc(sizeof(SYSBUS_SVC_PLD));
|
|
if (payloadCopy == NULL)
|
|
{
|
|
DBG_ERROR("Allocate memory for send payload failed.\n");
|
|
return ENOMEM;
|
|
}
|
|
memcpy(payloadCopy, payload, sizeof(SYSBUS_SVC_PLD));
|
|
// valid id starts from 0x01
|
|
if (CircQueuePush(sysMonitorInst->sendQueue[payload->nodeId-1], (void *)payloadCopy) == false)
|
|
{
|
|
DBG_WARNING("SystemMonitor::SEND Queue overflowed!\n");
|
|
free(payloadCopy);
|
|
return EQUEUE;
|
|
}
|
|
|
|
return SUCCESS;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the latest send payload.
|
|
*
|
|
* @param payload
|
|
*/
|
|
void SysMonitor_GetSendPayload(SYSBUS_SVC_PLD **payload, uint8_t dstId)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(payload != NULL);
|
|
|
|
// valid id starts from 0x01
|
|
if (CircQueuePop(sysMonitorInst->sendQueue[dstId-1], (void **)payload) == false)
|
|
{
|
|
*payload = NULL;
|
|
DBG_INFO("SystemMonitor::Empty sendQueue, nothing need sent.\n");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Update receive payload.
|
|
*
|
|
* @param payload
|
|
*/
|
|
int32_t SysMonitor_GenRecvRsp(SYSBUS_SVC_PLD *payload)
|
|
{
|
|
ASSERT(payload != NULL);
|
|
int32_t Status;
|
|
|
|
if (payload->dataId == DATA_ID_NONE || payload->dataLen > SYSBUS_PLD_MAX)
|
|
{
|
|
DBG_INFO("SystemMonitor::Drop invalid message.\n");
|
|
return EIGNORE;
|
|
}
|
|
|
|
Status = SystemBus_GenRecvRsp(sysbusDescPtr, payload);
|
|
|
|
return Status;
|
|
}
|
|
/** @} ServicePayload_API*/
|
|
|
|
/**
|
|
* @name Linklayer_Log_API
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Update link layer log
|
|
*
|
|
* @param log
|
|
*/
|
|
void SysMonitor_UpdateLinklayerLog(const LINKLOG *log)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(log != NULL);
|
|
uint8_t buffIndx;
|
|
buffIndx = GetWritable(sysMonitorInst->linkLogCtrl);
|
|
sysMonitorInst->linkLogCtrl.wBusy = buffIndx;
|
|
memcpy(&(sysMonitorInst->linkLog[buffIndx]), log, sizeof(LINKLOG));
|
|
sysMonitorInst->linkLogCtrl.latest = buffIndx;
|
|
sysMonitorInst->linkLogCtrl.wBusy = BUFF_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the latest link layer log
|
|
*
|
|
* @param log
|
|
*/
|
|
void SysMonitor_GetLinklayerLog(LINKLOG *log)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(log != NULL);
|
|
uint8_t buffIndx, buffIndex_old;
|
|
buffIndex_old = sysMonitorInst->linkLogCtrl.rBusy;
|
|
if (buffIndex_old != BUFF_NONE)
|
|
buffIndx = buffIndex_old; // Keep it busy
|
|
else
|
|
buffIndx = GetReadable(sysMonitorInst->linkLogCtrl);
|
|
sysMonitorInst->linkLogCtrl.rBusy = buffIndx;
|
|
memcpy(log, &(sysMonitorInst->linkLog[buffIndx]), sizeof(LINKLOG));
|
|
sysMonitorInst->linkLogCtrl.rBusy = buffIndex_old;
|
|
}
|
|
/** @} Linklayer_Log_API*/
|
|
|
|
/**
|
|
* @name PhysicalLayer_Log_API
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Update physical layer log
|
|
*
|
|
* @param log
|
|
*/
|
|
void SysMonitor_UpdatePhysicalLayerLog(const PHYLOG *log)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(log != NULL);
|
|
uint8_t buffIndx;
|
|
buffIndx = GetWritable(sysMonitorInst->phyLogCtrl);
|
|
sysMonitorInst->phyLogCtrl.wBusy = buffIndx;
|
|
memcpy(&(sysMonitorInst->phyLog[buffIndx]), log, sizeof(PHYLOG));
|
|
sysMonitorInst->phyLogCtrl.latest = buffIndx;
|
|
sysMonitorInst->phyLogCtrl.wBusy = BUFF_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the latest physical layer log
|
|
*
|
|
* @param log
|
|
*/
|
|
void SysMonitor_GetPhysicalLayerLog(PHYLOG *log)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(log != NULL);
|
|
uint8_t buffIndx, buffIndex_old;
|
|
buffIndex_old = sysMonitorInst->phyLogCtrl.rBusy;
|
|
if (buffIndex_old != BUFF_NONE)
|
|
buffIndx = buffIndex_old; // Keep it busy
|
|
else
|
|
buffIndx = GetReadable(sysMonitorInst->phyLogCtrl);
|
|
sysMonitorInst->phyLogCtrl.rBusy = buffIndx;
|
|
memcpy(log, &(sysMonitorInst->phyLog[buffIndx]), sizeof(PHYLOG));
|
|
sysMonitorInst->phyLogCtrl.rBusy = buffIndex_old;
|
|
}
|
|
/** @} */
|
|
|
|
/**
|
|
* @name HardwareState_Log_API
|
|
* @{
|
|
*/
|
|
|
|
/**
|
|
* @brief Log hardware running status.
|
|
*
|
|
* @param state
|
|
*/
|
|
void SysMonitor_UpdateHardwareStatus(const HWSTATE *state)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(state != NULL);
|
|
uint8_t buffIndx;
|
|
buffIndx = GetWritable(sysMonitorInst->hwStateCtrl);
|
|
sysMonitorInst->hwStateCtrl.wBusy = buffIndx;
|
|
memcpy(&(sysMonitorInst->hwState[buffIndx]), state, sizeof(HWSTATE));
|
|
sysMonitorInst->hwStateCtrl.latest = buffIndx;
|
|
sysMonitorInst->hwStateCtrl.wBusy = BUFF_NONE;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the latest hardware running status.
|
|
*
|
|
* @param state
|
|
*/
|
|
void SysMonitor_GetHardwareStatus(HWSTATE *state)
|
|
{
|
|
ASSERT(sysMonitorInst != NULL);
|
|
ASSERT(state != NULL);
|
|
uint8_t buffIndx, buffIndex_old;
|
|
buffIndex_old = sysMonitorInst->hwStateCtrl.rBusy;
|
|
if (buffIndex_old != BUFF_NONE)
|
|
buffIndx = buffIndex_old; // Keep it busy
|
|
else
|
|
buffIndx = GetReadable(sysMonitorInst->hwStateCtrl);
|
|
sysMonitorInst->hwStateCtrl.rBusy = buffIndx;
|
|
memcpy(state, &(sysMonitorInst->hwState[buffIndx]), sizeof(HWSTATE));
|
|
sysMonitorInst->hwStateCtrl.rBusy = buffIndex_old;
|
|
}
|
|
|
|
|
|
/** @} SystemBus*/
|