/*****************************************************************************/ /** * Description: System bus protocol implementation file. * * Date: 2023-05-06 10:13 * Last Edit Time: [2023-05-06 21:45] * * Revisions: Rev 0.0 * History: * [2023-05-06]: Initial version. * [2023-09-13]: Update to 485 protocol-20220816 * * Copyright(c) 2021 Beijing Institute of Technology. * Lab of Communication and Network * ******************************************************************************/ /***************************** Include Files *********************************/ #include #include #include #include #include "systembus_uart.h" #include "systembus.h" #include "util.h" #include "common.h" #include "error.h" #include "circ_queue.h" #include "../system_monitor.h" /************************** Constant Definitions *****************************/ // Common Field // -- Command word #define WD_CMD 0x00 // -- Production type #define WD_PRD 0x01 #define PRD_DL_K 0xDD #define PRD_DL_S 0xEE #define PRD_ALL 0xFF // Self-Test #define CMD_SELF_TEST 0xE1 // -- Request // -- -- -- // -- Response // -- -- Self-Test Result #define ST_RSP_LEN 16 #define WD_ST_RET 0x02 #define ST_RET_OK 0xAA #define ST_RET_ERROR 0x55 // -- -- Signal processor PS voltage #define WD_SP_PS_VOLT 0x03 // -- -- Signal processor PS AUX voltage #define WD_SP_PSAUX_VOLT 0x04 // -- -- Signal processor DDR voltage #define WD_SP_DDR_VOLT 0x05 // -- -- Signal processor PL voltage #define WD_SP_PL_VOLT 0x06 // -- -- Signal processor PL AUX voltage #define WD_SP_PLAUX_VOLT 0x07 // -- -- Signal processor Bram voltage #define WD_SP_BRAM_VOLT 0x08 // -- -- Signal processor temperature #define WD_SP_TEMP 0x09 // -- -- Signal processor antenna voltage status #define WD_ANT_VOLT 0x0A // -- -- Signal processor antenna RF temperature #define WD_ANT_RF_TEMP 0x0B // -- -- Signal processor antenna PLL lock status #define WD_ANT_RF_PLL 0x0C // -- -- Signal processor antenna MGC #define WD_ANT_MGC_0 0x0D #define WD_ANT_MGC_1 0x0E // -- -- Dummy must be 0x00 #define WD_ST_DUMMY 0x0F // Data Send Test Command #define CMD_DATA_TEST_A 0xE2 // -- Request #define DATA_TEST_REQ_A_LEN 68 // -- -- 68 byte payload // -- Response #define DATA_TEST_RSP_A_LEN 68 // -- -- 68 byte payload // Data Send Test Command #define CMD_DATA_TEST_B 0xF2 // -- Request #define DATA_TEST_REQ_B_LEN 170 // -- -- 170 byte payload // -- Response #define DATA_TEST_RSP_B_LEN 170 // -- -- 170 byte payload // Config load #define CMD_CONFIG 0xE3 // -- Request #define WD_EN_CMD 0x02 #define EN_CMD_ON_HIGH_POWER 0x66 #define EN_CMD_ON_LOW_POWER 0x88 #define WD_MODE_CMD 0x03 #define MODE_CMD_HEAD 0x11 #define MODE_CMD_MEMBER 0x00 // -- Response // -- -- none response // System info update #define CMD_SYS_INFO 0x33 #define SYS_INFO_OFFSET 2 // -- Request // -- -- 54 byte system info // -- Response // -- -- none response // Data Send Test Command #define CMD_FLJ_SEND 0xE4 #define FLJ_DATA_OFFSET 2 // -- Request // -- -- 78 byte payload // -- Response // -- -- none response // Data Send Test Command #define CMD_LJ_SEND 0xF4 #define LJ_DATA_OFFSET 2 // -- Request // -- -- 156 byte payload // -- Response // -- -- none response // FLJ Poll #define CMD_FLJ_POLL 0xE5 // -- Request #define WD_FLJ_POLL 0x02 #define FLJ_POLL_CODE 0x77 // -- Response #define FLJ_POLL_RESP_LEN 170 // -- -- received 170 byte payload // LJ Poll #define CMD_LJ_POLL 0xF5 // -- Request #define WD_LJ_POLL 0x02 #define LJ_POLL_CODE 0x77 // -- Response #define LJ_POLL_RESP_LEN 68 // -- -- received 68 byte payload // Remote Test FLJ #define CMD_FLJ_RM_TEST 0xE6 // -- Request #define WD_FLJ_RM_TEST 0x02 #define FLJ_RM_TEST_CODE 0x99 // -- Response #define FLJ_RM_TEST_RESP_LEN 158 // Remote Test LJ #define CMD_LJ_RM_TEST 0xF6 // -- Request #define WD_LJ_RM_TEST 0x02 #define LJ_RM_TEST_CODE 0x99 // -- Response #define LJ_RM_TEST_RESP_LEN 158 // My Production Type #define MY_PRD_ID PRD_DL_K /************************** Function Prototypes ******************************/ /**************************** Type Definitions *******************************/ /************************** Variable Definitions *****************************/ // TODO: better approach? #include "../networking/link_layer/link_layer.h" #include "../networking/phy_layer/phy_layer.h" extern L2_INST *linkInstPtr; extern L2_CONFIG linkConfig; inline bool SystemBus_IsReqPendding(SYSBUS *instPtr) { ASSERT(instPtr != NULL); return (!isCircQueueEmpty(instPtr->rxQueue)); } inline bool SystemBus_IsRsqPendding(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 Initialize System Bus instance. * * @param InstPtr A double pointer to System Bus instance. * @param config A pointer to System Bus configurations. * * @return None. * ******************************************************************************/ 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 FAILURE; } (*instPtr)->rxQueue = NULL; (*instPtr)->txQueue = NULL; (*instPtr)->uartInst = NULL; memcpy(&((*instPtr)->config), config, sizeof(SYSBUS_CONFIG)); uartConfig.baseAddr = config->baseAddr; Status = SysbusUart_Init(&((*instPtr)->uartInst), &uartConfig); if (Status != SUCCESS) { Status = ENOMEM; goto sysbus_init_error; } if (CircQueueCreate(&((*instPtr)->txQueue), (*instPtr)->config.txQueueMax) == false) { Status = ENOMEM; goto sysbus_init_error; } if (CircQueueCreate(&((*instPtr)->rxQueue), (*instPtr)->config.rxQueueMax) == false) { Status = ENOMEM; goto sysbus_init_error; } (*instPtr)->isRxDone = false; // nothing received. (*instPtr)->isTxDone = true; // nothing to be sent. return SUCCESS; sysbus_init_error: free((*instPtr)->uartInst); free((*instPtr)->txQueue); free((*instPtr)->rxQueue); free(*instPtr); return Status; } /*****************************************************************************/ /** * @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, *sysbusRsp; uint8_t *data; if (CircQueuePop(instPtr->rxQueue, (void **)(&sysbusReq)) == false) { DBG_WARNING("SystemBus::Nothing to be handled.\n"); return EIGNORE; } data = sysbusReq->data; if (data[WD_PRD] != MY_PRD_ID && data[WD_PRD] != PRD_ALL) { DBG_WARNING("SystemBus::Production code mismatch!\n"); return EIGNORE; } switch (data[WD_CMD]) { case CMD_SELF_TEST: { const HWSTATE *hwStatsPtr; sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for SELF TEST Response!\n"); Status = ENOMEM; break; } 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; hwStatsPtr = Phy_RefreshHwSate(linkInstPtr->config.phyInstPtr); if (linkInstPtr->isEnable == false) { sysbusRsp->data[WD_ST_RET] = (hwStatsPtr->isSpOk) ? ST_RET_OK : ST_RET_ERROR; } else { sysbusRsp->data[WD_ST_RET] = (hwStatsPtr->isSpOk && hwStatsPtr->isAntOk) ? ST_RET_OK : ST_RET_ERROR; } sysbusRsp->data[WD_SP_PS_VOLT] = hwStatsPtr->spPsVolt; sysbusRsp->data[WD_SP_PSAUX_VOLT] = hwStatsPtr->spPsAuxVolt; sysbusRsp->data[WD_SP_DDR_VOLT] = hwStatsPtr->spDdrVolt; sysbusRsp->data[WD_SP_PL_VOLT] = hwStatsPtr->spPlVolt; sysbusRsp->data[WD_SP_PLAUX_VOLT] = hwStatsPtr->spPlAuxVolt; sysbusRsp->data[WD_SP_BRAM_VOLT] = hwStatsPtr->spBramVolt; sysbusRsp->data[WD_SP_TEMP] = hwStatsPtr->spTemp; sysbusRsp->data[WD_ANT_VOLT] = hwStatsPtr->antVolt; sysbusRsp->data[WD_ANT_RF_TEMP] = hwStatsPtr->antTemp; sysbusRsp->data[WD_ANT_RF_PLL] = hwStatsPtr->antPll; sysbusRsp->data[WD_ANT_MGC_0] = hwStatsPtr->antMgc0; sysbusRsp->data[WD_ANT_MGC_1] = hwStatsPtr->antMgc1; sysbusRsp->data[WD_ST_DUMMY] = 0; // dummy byte should always be 0 if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false) { free(sysbusRsp); DBG_ERROR("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_DATA_TEST_A: { sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for DATA TEST A Response!\n"); Status = ENOMEM; break; } sysbusRsp->len = DATA_TEST_RSP_A_LEN; memcpy(sysbusRsp->data, sysbusReq->data, DATA_TEST_RSP_A_LEN); if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false) { free(sysbusRsp); DBG_ERROR("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_DATA_TEST_B: { sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for DATA TEST B Response!\n"); Status = ENOMEM; break; } sysbusRsp->len = DATA_TEST_RSP_B_LEN; memcpy(sysbusRsp->data, sysbusReq->data, DATA_TEST_RSP_B_LEN); if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false) { free(sysbusRsp); DBG_ERROR("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_CONFIG: { PHY_INST *phyRef = linkInstPtr->config.phyInstPtr; TMC_INST *tmcRef = linkInstPtr->config.tmcInstPtr; if (data[WD_MODE_CMD] == MODE_CMD_HEAD) { // omit unnecessary reconfiguration if (L2_GetNodeType(linkInstPtr) != L2_NODE_HEAD) { // head node config linkConfig.myType = L2_NODE_HEAD; linkConfig.myId = 1; Linklayer_Reconfig(linkInstPtr, &linkConfig); } } else if (data[WD_MODE_CMD] == MODE_CMD_MEMBER ) { // omit unnecessary reconfiguration if (L2_GetNodeType(linkInstPtr) != L2_NODE_MEMBER) { // member node config linkConfig.myType = L2_NODE_MEMBER; linkConfig.myId = 2; Linklayer_Reconfig(linkInstPtr, &linkConfig); } } else { // FIXME: to confirm // Halt linklayer Linklayer_Halt(linkInstPtr); DBG_ERROR("Unexpected config command 0x%02X:0x%02X", WD_MODE_CMD, data[WD_MODE_CMD]); Status = EPARAM; break; } if(data[WD_EN_CMD] == EN_CMD_ON_HIGH_POWER) { // high power config Phy_AntConfigMode(phyRef, PHY_ANT_MODE_HIGH_POWER); } else if(data[WD_EN_CMD] == EN_CMD_ON_LOW_POWER) { // low power config Phy_AntConfigMode(phyRef, PHY_ANT_MODE_LOW_POWER); } else { // silence config // FIXME: confirm // TODO: better approach? Linklayer_Halt(linkInstPtr); Phy_AntConfigMode(phyRef, PHY_ANT_MODE_SILENT); TIME_WORD pullTime = {0, 0}; pullTime.int_part = TimeCtrl_GetIntCnt(tmcRef) + 1; Status = Phy_SetTimeWord(phyRef, &pullTime, PHY_TIME_WORD_POLL); if (Status != SUCCESS) { DBG_ERROR("Physical set pull time failed, with error code: %" PRId32 ".\n", Status); } Phy_SetupNextSlot(phyRef, PHY_TOR_RX, L1_10KBPS_10MS_FRAME, NULL); break; } Linklayer_Start(linkInstPtr); Status = SUCCESS; break; } case CMD_SYS_INFO: { SysMonitor_UpdateSysinfoRaw(data + SYS_INFO_OFFSET); Status = SUCCESS; break; } // TODO case CMD_FLJ_SEND: { SysMonitor_UpdateSendPayloadRaw(data + FLJ_DATA_OFFSET, PAYLOAD_LEN_FLJ_TX); // omit control info break; } case CMD_LJ_SEND: { SysMonitor_UpdateSendPayloadRaw(data + LJ_DATA_OFFSET, PAYLOAD_LEN_LJ_TX); // omit control info break; } case CMD_FLJ_POLL: { if (data[WD_FLJ_POLL] != FLJ_POLL_CODE) { DBG_ERROR("SystemBus::Invalid roll call code!\n"); Status = EIGNORE; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for FLJ POLL Response!\n"); Status = ENOMEM; break; } sysbusRsp->len = FLJ_POLL_RESP_LEN; sysbusRsp->data[WD_CMD] = CMD_FLJ_POLL; sysbusRsp->data[WD_PRD] = MY_PRD_ID; uint32_t dataId; SysMonitor_GetRecvPayloadRaw(sysbusRsp->data + FLJ_DATA_OFFSET, &dataId, PAYLOAD_LEN_FLJ_RX); // omit control info if (dataId == DATA_ID_NONE) { free(sysbusRsp); DBG_WARNING("SystemBus::no RX data!\n"); Status = SUCCESS; break; } if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false) { free(sysbusRsp); DBG_WARNING("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_LJ_POLL: { if (data[WD_LJ_POLL] != LJ_POLL_CODE) { DBG_WARNING("SystemBus::Invalid roll call code!\n"); Status = EIGNORE; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for LJ POLL Response!\n"); Status = ENOMEM; break; } sysbusRsp->len = LJ_POLL_RESP_LEN; sysbusRsp->data[WD_CMD] = CMD_LJ_POLL; sysbusRsp->data[WD_PRD] = MY_PRD_ID; uint32_t dataId; SysMonitor_GetRecvPayloadRaw(sysbusRsp->data + LJ_DATA_OFFSET, &dataId, PAYLOAD_LEN_LJ_RX); // omit control info if (dataId == DATA_ID_NONE) { free(sysbusRsp); DBG_WARNING("SystemBus::no RX data!\n"); Status = SUCCESS; break; } if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false) { free(sysbusRsp); DBG_WARNING("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_FLJ_RM_TEST: { if (data[WD_FLJ_RM_TEST] != FLJ_RM_TEST_CODE) { DBG_WARNING("SystemBus::Invalid remote test code!\n"); Status = EIGNORE; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for FLJ REMOTE TEST Response!\n"); Status = ENOMEM; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); sysbusRsp->len = FLJ_RM_TEST_RESP_LEN; memset(sysbusRsp->data, 0x00, FLJ_RM_TEST_RESP_LEN); sysbusRsp->data[WD_CMD] = CMD_FLJ_RM_TEST; sysbusRsp->data[WD_PRD] = MY_PRD_ID; if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false){ free(sysbusRsp); DBG_WARNING("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } case CMD_LJ_RM_TEST: { if (data[WD_LJ_RM_TEST] != LJ_RM_TEST_CODE) { DBG_WARNING("SystemBus::Invalid remote test code!\n"); Status = EIGNORE; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusRsp == NULL) { DBG_ERROR("Failed to allocate memory for LJ REMOTE TEST Response!\n"); Status = ENOMEM; break; } sysbusRsp = malloc(sizeof(SYSBUS_UART_FRAME)); sysbusRsp->len = LJ_RM_TEST_RESP_LEN; memset(sysbusRsp->data, 0x00, LJ_RM_TEST_RESP_LEN); sysbusRsp->data[WD_CMD] = CMD_LJ_RM_TEST; sysbusRsp->data[WD_PRD] = MY_PRD_ID; if (CircQueuePush(instPtr->txQueue, (void *)sysbusRsp) == false){ free(sysbusRsp); DBG_WARNING("SystemBus::TX Queue overflowed!\n"); Status = EQUEUE; break; } Status = SUCCESS; break; } default: { DBG_ERROR("SystemBus::Unknown command code!\n"); 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 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); int32_t Status; SYSBUS_UART_FRAME *sysbusReq; instPtr->isRxDone = true; // whether reception successes or not, receiving procedure ended. sysbusReq = malloc(sizeof(SYSBUS_UART_FRAME)); if (sysbusReq == NULL) { return ENOMEM; } Status = SysbusUart_Recv(instPtr->uartInst, sysbusReq); if (Status != SUCCESS) { goto recv_error; } if (CircQueuePush(instPtr->rxQueue, (void *)sysbusReq) == false) { DBG_ERROR("SystemBus::RX Queue overflowed!\n"); Status = EQUEUE; goto recv_error; } instPtr->isRxDone = false; // another reception is undergoing. return SUCCESS; recv_error: free(sysbusReq); return Status; }