90cdc4920b
* Fixed MS/TP module flush stderr compile error from leftover. * Fixed MS/TP zero-config preferred-station setting to not filter getter.
1735 lines
77 KiB
C
1735 lines
77 KiB
C
/**
|
|
* @file
|
|
* @brief Implementation of the finite state machines
|
|
* and BACnet Master-Slave Twisted Pair (MS/TP) functions
|
|
* @author Steve Karg <skarg@users.sourceforge.net>
|
|
* @date 2003
|
|
* @details This clause describes a Master-Slave/Token-Passing (MS/TP)
|
|
* data link protocol, which provides the same services to the network layer
|
|
* as ISO 8802-2 Logical Link Control. It uses services provided by the
|
|
* EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be
|
|
* included in this standard by reference. The following hardware is assumed:
|
|
* (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of
|
|
* transmitting and receiving eight data bits with one stop bit
|
|
* and no parity.
|
|
* (b) An EIA-485 transceiver whose driver may be disabled.
|
|
* (c) A timer with a resolution of five milliseconds or less
|
|
*
|
|
* @copyright SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0
|
|
* @defgroup DLMSTP BACnet MS/TP DataLink Network Layer
|
|
* @ingroup DataLink
|
|
*/
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#if PRINT_ENABLED
|
|
#include <stdio.h>
|
|
#endif
|
|
#include "bacnet/datalink/cobs.h"
|
|
#include "bacnet/datalink/crc.h"
|
|
#include "bacnet/datalink/mstp.h"
|
|
#include "bacnet/datalink/crc.h"
|
|
#include "bacnet/datalink/mstptext.h"
|
|
#include "bacnet/npdu.h"
|
|
#include "bacnet/basic/sys/debug.h"
|
|
|
|
#if PRINT_ENABLED
|
|
#undef PRINT_ENABLED_RECEIVE
|
|
#undef PRINT_ENABLED_RECEIVE_DATA
|
|
#undef PRINT_ENABLED_RECEIVE_ERRORS
|
|
#undef PRINT_ENABLED_MASTER
|
|
#endif
|
|
|
|
#if defined(PRINT_ENABLED_RECEIVE)
|
|
#define printf_receive debug_printf
|
|
#else
|
|
static __inline__ void printf_receive(const char *format, ...)
|
|
{
|
|
(void)format;
|
|
}
|
|
#endif
|
|
|
|
#if defined(PRINT_ENABLED_RECEIVE_DATA)
|
|
#define printf_receive_data debug_printf
|
|
#else
|
|
static __inline__ void printf_receive_data(const char *format, ...)
|
|
{
|
|
(void)format;
|
|
}
|
|
#endif
|
|
|
|
#if defined(PRINT_ENABLED_RECEIVE_ERRORS)
|
|
#define printf_receive_error debug_printf
|
|
#else
|
|
static __inline__ void printf_receive_error(const char *format, ...)
|
|
{
|
|
(void)format;
|
|
}
|
|
#endif
|
|
|
|
#if defined(PRINT_ENABLED_MASTER)
|
|
#define printf_master debug_printf
|
|
#else
|
|
static __inline__ void printf_master(const char *format, ...)
|
|
{
|
|
(void)format;
|
|
}
|
|
#endif
|
|
|
|
/* MS/TP Frame Format */
|
|
/* All frames are of the following format: */
|
|
/* */
|
|
/* Preamble: two octet preamble: X`55', X`FF' */
|
|
/* Frame Type: one octet */
|
|
/* Destination Address: one octet address */
|
|
/* Source Address: one octet address */
|
|
/* Length: two octets, most significant octet first, of the Data field */
|
|
/* Header CRC: one octet */
|
|
/* Data: (present only if Length is non-zero) */
|
|
/* Data CRC: (present only if Length is non-zero) two octets, */
|
|
/* least significant octet first */
|
|
/* (pad): (optional) at most one octet of padding: X'FF' */
|
|
|
|
/* we need to be able to increment without rolling over */
|
|
#define INCREMENT_AND_LIMIT_UINT8(x) \
|
|
{ \
|
|
if (x < 0xFF) \
|
|
x++; \
|
|
}
|
|
|
|
bool MSTP_Line_Active(const struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
if (!mstp_port) {
|
|
return false;
|
|
}
|
|
|
|
return (mstp_port->EventCount > Nmin_octets);
|
|
}
|
|
|
|
void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!src) {
|
|
return;
|
|
}
|
|
if (mstp_address == MSTP_BROADCAST_ADDRESS) {
|
|
/* mac_len = 0 if broadcast address */
|
|
src->mac_len = 0;
|
|
src->mac[0] = 0;
|
|
} else {
|
|
src->mac_len = 1;
|
|
src->mac[0] = mstp_address;
|
|
}
|
|
/* fill with 0's starting with index 1; index 0 filled above */
|
|
for (i = 1; i < MAX_MAC_LEN; i++) {
|
|
src->mac[i] = 0;
|
|
}
|
|
src->net = 0;
|
|
src->len = 0;
|
|
for (i = 0; i < MAX_MAC_LEN; i++) {
|
|
src->adr[i] = 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Create an MS/TP Frame
|
|
*
|
|
* All MS/TP frames are of the following format:
|
|
* Preamble: two octet preamble: X`55', X`FF'
|
|
* Frame Type: one octet
|
|
* Destination Address: one octet address
|
|
* Source Address: one octet address
|
|
* Length: two octets, most significant octet first, of the Data field
|
|
* Header CRC: one octet
|
|
* Data: (present only if Length is non-zero)
|
|
* Data CRC: (present only if Length is non-zero) two octets,
|
|
* least significant octet first
|
|
* (pad): (optional) at most one octet of padding: X'FF'
|
|
*
|
|
* @param buffer - where frame is loaded
|
|
* @param buffer_size - amount of space available in the buffer
|
|
* @param frame_type - type of frame to send - see defines
|
|
* @param destination - destination address
|
|
* @param source - source address
|
|
* @param data - any data to be sent - may be null
|
|
* @param data_len - number of bytes of data
|
|
* @return number of bytes encoded, or 0 on error
|
|
*/
|
|
uint16_t MSTP_Create_Frame(
|
|
uint8_t *buffer,
|
|
uint16_t buffer_size,
|
|
uint8_t frame_type,
|
|
uint8_t destination,
|
|
uint8_t source,
|
|
const uint8_t *data,
|
|
uint16_t data_len)
|
|
{
|
|
uint8_t crc8 = 0xFF; /* used to calculate the crc value */
|
|
uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */
|
|
uint16_t index = 0; /* used to load the data portion of the frame */
|
|
uint16_t cobs_len; /* length of the COBS encoded frame */
|
|
bool cobs_bacnet_frame = false; /* true for COBS BACnet frames */
|
|
|
|
if (!buffer) {
|
|
return 0;
|
|
}
|
|
/* encode the data portion of the packet */
|
|
if ((data_len > MSTP_FRAME_NPDU_MAX) ||
|
|
((frame_type >= Nmin_COBS_type) && (frame_type <= Nmax_COBS_type))) {
|
|
/* COBS encoded frame */
|
|
if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) {
|
|
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY;
|
|
cobs_bacnet_frame = true;
|
|
} else if (frame_type == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY) {
|
|
frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY;
|
|
cobs_bacnet_frame = true;
|
|
} else if (
|
|
(frame_type < Nmin_COBS_type) || (frame_type > Nmax_COBS_type)) {
|
|
/* I'm sorry, Dave, I'm afraid I can't do that. */
|
|
return 0;
|
|
}
|
|
cobs_len =
|
|
cobs_frame_encode(&buffer[8], buffer_size - 8, data, data_len);
|
|
/* check the results of COBs encoding for validity */
|
|
if (cobs_bacnet_frame) {
|
|
if (cobs_len < Nmin_COBS_length_BACnet) {
|
|
return 0;
|
|
} else if (cobs_len > Nmax_COBS_length_BACnet) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
if (cobs_len < Nmin_COBS_length) {
|
|
return 0;
|
|
} else if (cobs_len > Nmax_COBS_length) {
|
|
return 0;
|
|
}
|
|
}
|
|
/* for COBS, we must subtract two before use as the
|
|
MS/TP frame length field since CRC32 is 2 bytes longer
|
|
than CRC16 in original MSTP and non-COBS devices need
|
|
to be able to ingest the entire frame */
|
|
data_len = cobs_len - 2;
|
|
} else if (data_len > 0) {
|
|
if (!data) {
|
|
return 0;
|
|
}
|
|
if ((8 + data_len + 2) > buffer_size) {
|
|
return 0;
|
|
}
|
|
for (index = 8; index < (data_len + 8); index++, data++) {
|
|
buffer[index] = *data;
|
|
crc16 = CRC_Calc_Data(buffer[index], crc16);
|
|
}
|
|
crc16 = ~crc16;
|
|
buffer[index] = crc16 & 0xFF; /* LSB first */
|
|
buffer[index + 1] = crc16 >> 8;
|
|
}
|
|
buffer[0] = 0x55;
|
|
buffer[1] = 0xFF;
|
|
buffer[2] = frame_type;
|
|
crc8 = CRC_Calc_Header(buffer[2], crc8);
|
|
buffer[3] = destination;
|
|
crc8 = CRC_Calc_Header(buffer[3], crc8);
|
|
buffer[4] = source;
|
|
crc8 = CRC_Calc_Header(buffer[4], crc8);
|
|
buffer[5] = data_len >> 8; /* MSB first */
|
|
crc8 = CRC_Calc_Header(buffer[5], crc8);
|
|
buffer[6] = data_len & 0xFF;
|
|
crc8 = CRC_Calc_Header(buffer[6], crc8);
|
|
buffer[7] = ~crc8;
|
|
index = 8;
|
|
if (data_len > 0) {
|
|
index += data_len + 2;
|
|
}
|
|
|
|
return index; /* returns the frame length */
|
|
}
|
|
|
|
/**
|
|
* @brief Send an MS/TP Frame
|
|
* @param mstp_port - port to send from
|
|
* @param frame_type - type of frame to send - see defines
|
|
* @param destination - destination address
|
|
* @param source - source address
|
|
* @param data - any data to be sent - may be null
|
|
* @param data_len - number of bytes of data
|
|
*/
|
|
void MSTP_Create_And_Send_Frame(
|
|
struct mstp_port_struct_t *mstp_port,
|
|
uint8_t frame_type,
|
|
uint8_t destination,
|
|
uint8_t source,
|
|
const uint8_t *data,
|
|
uint16_t data_len)
|
|
{
|
|
uint16_t len = 0; /* number of bytes to send */
|
|
|
|
len = MSTP_Create_Frame(
|
|
mstp_port->OutputBuffer, mstp_port->OutputBufferSize, frame_type,
|
|
destination, source, data, data_len);
|
|
|
|
MSTP_Send_Frame(mstp_port, &mstp_port->OutputBuffer[0], len);
|
|
/* FIXME: be sure to reset SilenceTimer() after each octet is sent! */
|
|
}
|
|
|
|
static bool MSTP_Frame_For_Us(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
if ((mstp_port->DestinationAddress == mstp_port->This_Station) ||
|
|
(mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS) ||
|
|
(mstp_port->This_Station == MSTP_BROADCAST_ADDRESS)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief Finite State Machine for receiving an MSTP frame
|
|
* @param mstp_port MSTP port context data
|
|
*/
|
|
void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
MSTP_RECEIVE_STATE receive_state = mstp_port->receive_state;
|
|
|
|
printf_receive(
|
|
"MSTP Rx: State=%s Data=%02X hCRC=%02X Index=%u EC=%u DateLen=%u "
|
|
"Silence=%u\n",
|
|
mstptext_receive_state(mstp_port->receive_state),
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index,
|
|
mstp_port->EventCount, mstp_port->DataLength,
|
|
mstp_port->SilenceTimer((void *)mstp_port));
|
|
switch (mstp_port->receive_state) {
|
|
case MSTP_RECEIVE_STATE_IDLE:
|
|
/* In the IDLE state, the node waits for
|
|
the beginning of a frame. */
|
|
/* EatAnError */
|
|
if (mstp_port->ReceiveError == true) {
|
|
mstp_port->ReceiveError = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
} else if (mstp_port->DataAvailable == true) {
|
|
/* wait for the start of a frame. */
|
|
printf_receive_data("MSTP Rx: %02X ", mstp_port->DataRegister);
|
|
if (mstp_port->DataRegister == 0x55) {
|
|
/* Preamble1 */
|
|
/* receive the remainder of the frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
|
|
} else {
|
|
/* EatAnOctet */
|
|
printf_receive_data("\n");
|
|
/* wait for the start of a frame. */
|
|
}
|
|
mstp_port->DataAvailable = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
}
|
|
break;
|
|
case MSTP_RECEIVE_STATE_PREAMBLE:
|
|
/* In the PREAMBLE state, the node waits for
|
|
the second octet of the preamble. */
|
|
/* Timeout */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Tframe_abort) {
|
|
/* a correct preamble has not been received */
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else if (mstp_port->ReceiveError == true) {
|
|
/* Error */
|
|
mstp_port->ReceiveError = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else if (mstp_port->DataAvailable == true) {
|
|
printf_receive_data("%02X ", mstp_port->DataRegister);
|
|
if (mstp_port->DataRegister == 0xFF) {
|
|
/* Preamble2 */
|
|
mstp_port->Index = 0;
|
|
mstp_port->HeaderCRC = 0xFF;
|
|
/* receive the remainder of the frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
|
|
} else if (mstp_port->DataRegister == 0x55) {
|
|
/* ignore RepeatedPreamble1 */
|
|
/* wait for the second preamble octet. */
|
|
} else {
|
|
/* NotPreamble */
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
}
|
|
mstp_port->DataAvailable = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
}
|
|
break;
|
|
case MSTP_RECEIVE_STATE_HEADER:
|
|
/* In the HEADER state, the node waits for
|
|
the fixed message header. */
|
|
/* Timeout */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Tframe_abort) {
|
|
/* indicate that an error has occurred during the reception of a
|
|
* frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
printf_receive_error(
|
|
"MSTP: Rx Header: SilenceTimer %u > %d\n",
|
|
(unsigned)mstp_port->SilenceTimer((void *)mstp_port),
|
|
mstp_port->Tframe_abort);
|
|
} else if (mstp_port->ReceiveError == true) {
|
|
/* Error */
|
|
mstp_port->ReceiveError = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
/* indicate that an error has occurred during the reception of a
|
|
* frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error("MSTP: Rx Header: ReceiveError\n");
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else if (mstp_port->DataAvailable == true) {
|
|
printf_receive_data("%02X ", mstp_port->DataRegister);
|
|
if (mstp_port->Index == 0) {
|
|
/* FrameType */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->FrameType = mstp_port->DataRegister;
|
|
mstp_port->Index = 1;
|
|
} else if (mstp_port->Index == 1) {
|
|
/* Destination */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->DestinationAddress = mstp_port->DataRegister;
|
|
mstp_port->Index = 2;
|
|
} else if (mstp_port->Index == 2) {
|
|
/* Source */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->SourceAddress = mstp_port->DataRegister;
|
|
mstp_port->Index = 3;
|
|
} else if (mstp_port->Index == 3) {
|
|
/* Length1 */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->DataLength = mstp_port->DataRegister * 256;
|
|
mstp_port->Index = 4;
|
|
} else if (mstp_port->Index == 4) {
|
|
/* Length2 */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->DataLength += mstp_port->DataRegister;
|
|
mstp_port->Index = 5;
|
|
} else if (mstp_port->Index == 5) {
|
|
/* HeaderCRC */
|
|
mstp_port->HeaderCRC = CRC_Calc_Header(
|
|
mstp_port->DataRegister, mstp_port->HeaderCRC);
|
|
mstp_port->HeaderCRCActual = mstp_port->DataRegister;
|
|
/* don't wait for next state - do it here */
|
|
if (mstp_port->HeaderCRC != 0x55) {
|
|
/* BadCRC */
|
|
/* indicate that an error has occurred during
|
|
the reception of a frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error(
|
|
"MSTP: Rx Header: BadCRC [%02X]\n",
|
|
mstp_port->DataRegister);
|
|
/* wait for the start of the next frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else {
|
|
if (mstp_port->DataLength == 0) {
|
|
/* NoData */
|
|
if (MSTP_Frame_For_Us(mstp_port)) {
|
|
printf_receive_data(
|
|
"%s",
|
|
mstptext_frame_type(
|
|
(unsigned)mstp_port->FrameType));
|
|
/* indicate that a frame with no data has been
|
|
* received */
|
|
mstp_port->ReceivedValidFrame = true;
|
|
} else {
|
|
/* NotForUs */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
}
|
|
/* wait for the start of the next frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else {
|
|
if (MSTP_Frame_For_Us(mstp_port)) {
|
|
if (mstp_port->DataLength <=
|
|
mstp_port->InputBufferSize) {
|
|
/* Data */
|
|
mstp_port->receive_state =
|
|
MSTP_RECEIVE_STATE_DATA;
|
|
} else {
|
|
/* FrameTooLong */
|
|
printf_receive_error(
|
|
"MSTP: Rx Header: FrameTooLong %u\n",
|
|
(unsigned)mstp_port->DataLength);
|
|
mstp_port->receive_state =
|
|
MSTP_RECEIVE_STATE_SKIP_DATA;
|
|
}
|
|
} else {
|
|
/* DataNotForUs */
|
|
mstp_port->receive_state =
|
|
MSTP_RECEIVE_STATE_SKIP_DATA;
|
|
}
|
|
mstp_port->Index = 0;
|
|
mstp_port->DataCRC = 0xFFFF;
|
|
}
|
|
}
|
|
} else {
|
|
/* not per MS/TP standard, but it is a case not covered */
|
|
mstp_port->ReceiveError = false;
|
|
/* indicate that an error has occurred during */
|
|
/* the reception of a frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error(
|
|
"MSTP: Rx Data: BadIndex %u\n",
|
|
(unsigned)mstp_port->Index);
|
|
/* wait for the start of a frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
}
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
|
|
mstp_port->DataAvailable = false;
|
|
}
|
|
break;
|
|
case MSTP_RECEIVE_STATE_DATA:
|
|
case MSTP_RECEIVE_STATE_SKIP_DATA:
|
|
/* In the DATA and SKIP DATA states, the node waits for the
|
|
data portion of a frame. */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Tframe_abort) {
|
|
/* Timeout */
|
|
/* indicate that an error has occurred during the reception of a
|
|
* frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error(
|
|
"MSTP: Rx Data: SilenceTimer %ums > %dms\n",
|
|
(unsigned)mstp_port->SilenceTimer((void *)mstp_port),
|
|
mstp_port->Tframe_abort);
|
|
/* wait for the start of the next frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else if (mstp_port->ReceiveError == true) {
|
|
/* Error */
|
|
mstp_port->ReceiveError = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
/* indicate that an error has occurred during the reception of a
|
|
* frame */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error("MSTP: Rx Data: ReceiveError\n");
|
|
/* wait for the start of the next frame. */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else if (mstp_port->DataAvailable == true) {
|
|
printf_receive_data("%02X ", mstp_port->DataRegister);
|
|
if (mstp_port->Index < mstp_port->DataLength) {
|
|
/* DataOctet */
|
|
mstp_port->DataCRC = CRC_Calc_Data(
|
|
mstp_port->DataRegister, mstp_port->DataCRC);
|
|
if (mstp_port->Index < mstp_port->InputBufferSize) {
|
|
mstp_port->InputBuffer[mstp_port->Index] =
|
|
mstp_port->DataRegister;
|
|
}
|
|
mstp_port->Index++;
|
|
/* SKIP_DATA or DATA - no change in state */
|
|
} else if (mstp_port->Index == mstp_port->DataLength) {
|
|
/* CRC1 */
|
|
mstp_port->DataCRC = CRC_Calc_Data(
|
|
mstp_port->DataRegister, mstp_port->DataCRC);
|
|
mstp_port->DataCRCActualMSB = mstp_port->DataRegister;
|
|
if (mstp_port->Index < mstp_port->InputBufferSize) {
|
|
mstp_port->InputBuffer[mstp_port->Index] =
|
|
mstp_port->DataRegister;
|
|
}
|
|
mstp_port->Index++;
|
|
/* SKIP_DATA or DATA - no change in state */
|
|
} else if (mstp_port->Index == (mstp_port->DataLength + 1)) {
|
|
/* CRC2 */
|
|
if (mstp_port->Index < mstp_port->InputBufferSize) {
|
|
mstp_port->InputBuffer[mstp_port->Index] =
|
|
mstp_port->DataRegister;
|
|
}
|
|
mstp_port->DataCRC = CRC_Calc_Data(
|
|
mstp_port->DataRegister, mstp_port->DataCRC);
|
|
mstp_port->DataCRCActualLSB = mstp_port->DataRegister;
|
|
printf_receive_data(
|
|
"%s",
|
|
mstptext_frame_type((unsigned)mstp_port->FrameType));
|
|
if (((mstp_port->Index + 1) < mstp_port->InputBufferSize) &&
|
|
(mstp_port->FrameType >= Nmin_COBS_type) &&
|
|
(mstp_port->FrameType <= Nmax_COBS_type)) {
|
|
mstp_port->DataLength = cobs_frame_decode(
|
|
&mstp_port->InputBuffer[mstp_port->Index + 1],
|
|
mstp_port->InputBufferSize, mstp_port->InputBuffer,
|
|
mstp_port->Index + 1);
|
|
if ((mstp_port->DataLength > 0) &&
|
|
(mstp_port->receive_state ==
|
|
MSTP_RECEIVE_STATE_DATA)) {
|
|
/* GoodCRC */
|
|
mstp_port->ReceivedValidFrame = true;
|
|
} else {
|
|
/* Done */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
}
|
|
} else {
|
|
/* STATE DATA CRC - no need for new state */
|
|
if (mstp_port->DataCRC == 0xF0B8) {
|
|
if (mstp_port->receive_state ==
|
|
MSTP_RECEIVE_STATE_DATA) {
|
|
/* GoodCRC */
|
|
mstp_port->ReceivedValidFrame = true;
|
|
} else {
|
|
/* Done */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
}
|
|
} else {
|
|
/* BadCRC */
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
printf_receive_error(
|
|
"MSTP: Rx Data: BadCRC [%02X]\n",
|
|
mstp_port->DataRegister);
|
|
}
|
|
}
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
} else {
|
|
mstp_port->ReceivedInvalidFrame = true;
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
}
|
|
mstp_port->DataAvailable = false;
|
|
mstp_port->SilenceTimerReset((void *)mstp_port);
|
|
}
|
|
break;
|
|
default:
|
|
/* shouldn't get here - but if we do... */
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
break;
|
|
}
|
|
if ((receive_state != MSTP_RECEIVE_STATE_IDLE) &&
|
|
(mstp_port->receive_state == MSTP_RECEIVE_STATE_IDLE)) {
|
|
printf_receive_data("\n");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* @brief Finite State Machine for receiving an MSTP frame
|
|
* @param mstp_port MSTP port context data
|
|
* @return true if we need to transition immediately
|
|
*/
|
|
bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
unsigned length = 0;
|
|
uint8_t next_poll_station = 0;
|
|
uint8_t next_this_station = 0;
|
|
uint8_t next_next_station = 0;
|
|
uint16_t my_timeout = 10, ns_timeout = 0, mm_timeout = 0;
|
|
/* transition immediately to the next state */
|
|
bool transition_now = false;
|
|
MSTP_MASTER_STATE master_state = mstp_port->master_state;
|
|
|
|
/* some calculations that several states need */
|
|
next_poll_station =
|
|
(mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1);
|
|
next_this_station =
|
|
(mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1);
|
|
next_next_station =
|
|
(mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1);
|
|
/* The zero config checks before running FSM */
|
|
if ((mstp_port->ZeroConfigEnabled) &&
|
|
(mstp_port->master_state != MSTP_MASTER_STATE_INITIALIZE) &&
|
|
(mstp_port->ReceivedValidFrame == true) &&
|
|
(mstp_port->SourceAddress == mstp_port->This_Station)) {
|
|
/* DuplicateNode */
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_INIT;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
|
|
/* ignore the frame */
|
|
mstp_port->ReceivedValidFrame = false;
|
|
}
|
|
switch (mstp_port->master_state) {
|
|
case MSTP_MASTER_STATE_INITIALIZE:
|
|
if (mstp_port->ZeroConfigEnabled) {
|
|
MSTP_Zero_Config_FSM(mstp_port);
|
|
if (mstp_port->This_Station != 255) {
|
|
/* indicate that the next station is unknown */
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
/* Send a Poll For Master since we just received
|
|
the token */
|
|
mstp_port->Poll_Station = (mstp_port->Next_Station + 1) %
|
|
(mstp_port->Zero_Config_Max_Master + 1);
|
|
mstp_port->TokenCount = Npoll;
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->EventCount = 0;
|
|
mstp_port->SoleMaster = true;
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL,
|
|
0);
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
transition_now = true;
|
|
}
|
|
} else {
|
|
/* DoneInitializing */
|
|
/* indicate that the next station is unknown */
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
mstp_port->Poll_Station = mstp_port->This_Station;
|
|
/* cause a Poll For Master to be sent when this node first */
|
|
/* receives the token */
|
|
mstp_port->TokenCount = Npoll;
|
|
mstp_port->SoleMaster = false;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
transition_now = true;
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_IDLE:
|
|
/* In the IDLE state, the node waits for a frame. */
|
|
if (mstp_port->ReceivedInvalidFrame == true) {
|
|
/* ReceivedInvalidFrame */
|
|
/* invalid frame was received */
|
|
/* wait for the next frame - remain in IDLE */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (mstp_port->ReceivedValidFrame == true) {
|
|
printf_master(
|
|
"MSTP: ReceivedValidFrame "
|
|
"Src=%02X Dest=%02X DataLen=%u "
|
|
"FC=%u ST=%u Type=%s\n",
|
|
mstp_port->SourceAddress, mstp_port->DestinationAddress,
|
|
mstp_port->DataLength, mstp_port->FrameCount,
|
|
mstp_port->SilenceTimer((void *)mstp_port),
|
|
mstptext_frame_type((unsigned)mstp_port->FrameType));
|
|
/* destined for me! */
|
|
switch (mstp_port->FrameType) {
|
|
case FRAME_TYPE_TOKEN:
|
|
/* ReceivedToken */
|
|
/* tokens cannot be broadcast */
|
|
if (mstp_port->DestinationAddress ==
|
|
MSTP_BROADCAST_ADDRESS) {
|
|
break;
|
|
}
|
|
mstp_port->ReceivedValidFrame = false;
|
|
mstp_port->FrameCount = 0;
|
|
mstp_port->SoleMaster = false;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
|
|
transition_now = true;
|
|
break;
|
|
case FRAME_TYPE_POLL_FOR_MASTER:
|
|
/* ReceivedPFM */
|
|
/* DestinationAddress is equal to TS */
|
|
if (mstp_port->DestinationAddress ==
|
|
mstp_port->This_Station) {
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
|
|
mstp_port->SourceAddress,
|
|
mstp_port->This_Station, NULL, 0);
|
|
}
|
|
break;
|
|
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
|
case FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY:
|
|
if ((mstp_port->DestinationAddress ==
|
|
MSTP_BROADCAST_ADDRESS) &&
|
|
(npdu_confirmed_service(
|
|
mstp_port->InputBuffer,
|
|
mstp_port->DataLength))) {
|
|
/* BTL test: verifies that the IUT will
|
|
quietly discard any Confirmed-Request-PDU,
|
|
whose destination address is a multicast or
|
|
broadcast address, received from the
|
|
network layer. */
|
|
} else {
|
|
/* ForUs */
|
|
/* indicate successful reception
|
|
to the higher layers */
|
|
(void)MSTP_Put_Receive(mstp_port);
|
|
}
|
|
break;
|
|
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
|
case FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY:
|
|
if (mstp_port->DestinationAddress ==
|
|
MSTP_BROADCAST_ADDRESS) {
|
|
/* broadcast DER just remains IDLE */
|
|
} else {
|
|
/* indicate successful reception to the higher
|
|
* layers */
|
|
(void)MSTP_Put_Receive(mstp_port);
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST;
|
|
}
|
|
break;
|
|
case FRAME_TYPE_TEST_REQUEST:
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TEST_RESPONSE,
|
|
mstp_port->SourceAddress, mstp_port->This_Station,
|
|
mstp_port->InputBuffer, mstp_port->DataLength);
|
|
break;
|
|
case FRAME_TYPE_TEST_RESPONSE:
|
|
default:
|
|
break;
|
|
}
|
|
/* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for
|
|
reference, and the flag will be cleared in the next state */
|
|
if (mstp_port->master_state !=
|
|
MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
}
|
|
} else if (
|
|
mstp_port->SilenceTimer((void *)mstp_port) >= Tno_token) {
|
|
/* LostToken */
|
|
/* assume that the token has been lost */
|
|
mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN;
|
|
/* set the receive frame flags to false in case we received
|
|
some bytes and had a timeout for some reason */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
mstp_port->ReceivedValidFrame = false;
|
|
transition_now = true;
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_USE_TOKEN:
|
|
/* In the USE_TOKEN state, the node is allowed to send one or */
|
|
/* more data frames. These may be BACnet Data frames or */
|
|
/* proprietary frames. */
|
|
/* FIXME: We could wait for up to Tusage_delay */
|
|
length = (unsigned)MSTP_Get_Send(mstp_port, 0);
|
|
if (length < 1) {
|
|
/* NothingToSend */
|
|
mstp_port->FrameCount = mstp_port->Nmax_info_frames;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
transition_now = true;
|
|
} else {
|
|
uint8_t frame_type = mstp_port->OutputBuffer[2];
|
|
uint8_t destination = mstp_port->OutputBuffer[3];
|
|
MSTP_Send_Frame(
|
|
mstp_port, &mstp_port->OutputBuffer[0], (uint16_t)length);
|
|
mstp_port->FrameCount++;
|
|
switch (frame_type) {
|
|
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
|
if (destination == MSTP_BROADCAST_ADDRESS) {
|
|
/* SendNoWait */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
} else {
|
|
/* SendAndWait */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_WAIT_FOR_REPLY;
|
|
}
|
|
break;
|
|
case FRAME_TYPE_TEST_REQUEST:
|
|
/* SendAndWait */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_WAIT_FOR_REPLY;
|
|
break;
|
|
case FRAME_TYPE_TEST_RESPONSE:
|
|
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
|
default:
|
|
/* SendNoWait */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
|
|
/* In the WAIT_FOR_REPLY state, the node waits for */
|
|
/* a reply from another node. */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >=
|
|
mstp_port->Treply_timeout) {
|
|
/* ReplyTimeout */
|
|
/* assume that the request has failed */
|
|
mstp_port->FrameCount = mstp_port->Nmax_info_frames;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
/* Any retry of the data frame shall await the next entry */
|
|
/* to the USE_TOKEN state. (Because of the length of the
|
|
* timeout, */
|
|
/* this transition will cause the token to be passed regardless
|
|
*/
|
|
/* of the initial value of FrameCount.) */
|
|
transition_now = true;
|
|
} else {
|
|
if (mstp_port->ReceivedInvalidFrame == true) {
|
|
/* InvalidFrame */
|
|
/* error in frame reception */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
transition_now = true;
|
|
} else if (mstp_port->ReceivedValidFrame == true) {
|
|
if (mstp_port->DestinationAddress ==
|
|
mstp_port->This_Station) {
|
|
switch (mstp_port->FrameType) {
|
|
case FRAME_TYPE_REPLY_POSTPONED:
|
|
/* ReceivedReplyPostponed */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
break;
|
|
case FRAME_TYPE_TEST_RESPONSE:
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
break;
|
|
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
|
/* ReceivedReply */
|
|
/* or a proprietary type that indicates a reply
|
|
*/
|
|
/* indicate successful reception to the higher
|
|
* layers */
|
|
(void)MSTP_Put_Receive(mstp_port);
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_DONE_WITH_TOKEN;
|
|
break;
|
|
default:
|
|
/* if proprietary frame was expected, you might
|
|
need to transition to DONE WITH TOKEN */
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_IDLE;
|
|
break;
|
|
}
|
|
} else {
|
|
/* ReceivedUnexpectedFrame */
|
|
/* an unexpected frame was received */
|
|
/* This may indicate the presence of multiple tokens. */
|
|
/* Synchronize with the network. */
|
|
/* This action drops the token. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
}
|
|
mstp_port->ReceivedValidFrame = false;
|
|
transition_now = true;
|
|
}
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_DONE_WITH_TOKEN:
|
|
/* The DONE_WITH_TOKEN state either sends another data frame, */
|
|
/* passes the token, or initiates a Poll For Master cycle. */
|
|
/* SendAnotherFrame */
|
|
if (mstp_port->FrameCount < mstp_port->Nmax_info_frames) {
|
|
/* then this node may send another information frame */
|
|
/* before passing the token. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
|
|
transition_now = true;
|
|
} else if (
|
|
(mstp_port->SoleMaster == false) &&
|
|
(mstp_port->Next_Station == mstp_port->This_Station)) {
|
|
/* NextStationUnknown - added in Addendum 135-2008v-1 */
|
|
/* then the next station to which the token
|
|
should be sent is unknown - so PollForMaster */
|
|
mstp_port->Poll_Station = next_this_station;
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
} else if (mstp_port->TokenCount < (Npoll - 1)) {
|
|
/* Npoll changed in Errata SSPC-135-2004 */
|
|
if ((mstp_port->SoleMaster == true) &&
|
|
(mstp_port->Next_Station != next_this_station)) {
|
|
/* SoleMaster */
|
|
/* there are no other known master nodes to */
|
|
/* which the token may be sent (true master-slave
|
|
* operation). */
|
|
mstp_port->FrameCount = 0;
|
|
mstp_port->TokenCount++;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
|
|
transition_now = true;
|
|
} else {
|
|
/* SendToken */
|
|
/* Npoll changed in Errata SSPC-135-2004 */
|
|
/* The comparison of NS and TS+1 eliminates the Poll For
|
|
* Master */
|
|
/* if there are no addresses between TS and NS, since there
|
|
* is no */
|
|
/* address at which a new master node may be found in that
|
|
* case. */
|
|
mstp_port->TokenCount++;
|
|
/* transmit a Token frame to NS */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TOKEN, mstp_port->Next_Station,
|
|
mstp_port->This_Station, NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->EventCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
|
|
}
|
|
} else if (next_poll_station == mstp_port->Next_Station) {
|
|
if (mstp_port->SoleMaster == true) {
|
|
/* SoleMasterRestartMaintenancePFM */
|
|
mstp_port->Poll_Station = next_next_station;
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL,
|
|
0);
|
|
/* no known successor node */
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
mstp_port->RetryCount = 0;
|
|
/* changed in Errata SSPC-135-2004 */
|
|
mstp_port->TokenCount = 1;
|
|
/* mstp_port->EventCount = 0; removed in Addendum
|
|
* 135-2004d-8 */
|
|
/* find a new successor to TS */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
} else {
|
|
/* ResetMaintenancePFM */
|
|
mstp_port->Poll_Station = mstp_port->This_Station;
|
|
/* transmit a Token frame to NS */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TOKEN, mstp_port->Next_Station,
|
|
mstp_port->This_Station, NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
/* changed in Errata SSPC-135-2004 */
|
|
mstp_port->TokenCount = 1;
|
|
mstp_port->EventCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
|
|
}
|
|
} else {
|
|
/* SendMaintenancePFM */
|
|
mstp_port->Poll_Station = next_poll_station;
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_PASS_TOKEN:
|
|
/* The PASS_TOKEN state listens for a successor to begin using */
|
|
/* the token that this node has just attempted to pass. */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) <=
|
|
mstp_port->Tusage_timeout) {
|
|
if (mstp_port->EventCount > Nmin_octets) {
|
|
/* SawTokenUser */
|
|
/* Assume that a frame has been sent by the new token user.
|
|
*/
|
|
/* Enter the IDLE state to process the frame. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
transition_now = true;
|
|
}
|
|
} else {
|
|
if (mstp_port->RetryCount < Nretry_token) {
|
|
/* RetrySendToken */
|
|
mstp_port->RetryCount++;
|
|
/* Transmit a Token frame to NS */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TOKEN, mstp_port->Next_Station,
|
|
mstp_port->This_Station, NULL, 0);
|
|
mstp_port->EventCount = 0;
|
|
/* re-enter the current state to listen for NS */
|
|
/* to begin using the token. */
|
|
} else {
|
|
/* FindNewSuccessor */
|
|
/* Assume that NS has failed. */
|
|
/* note: if NS=TS-1, this node could send PFM to self! */
|
|
mstp_port->Poll_Station = next_next_station;
|
|
/* Transmit a Poll For Master frame to PS. */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL,
|
|
0);
|
|
/* no known successor node */
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->TokenCount = 0;
|
|
/* mstp_port->EventCount = 0; removed in Addendum
|
|
* 135-2004d-8 */
|
|
/* find a new successor to TS */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
}
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_NO_TOKEN:
|
|
/* The NO_TOKEN state is entered if mstp_port->SilenceTimer()
|
|
* becomes greater */
|
|
/* than Tno_token, indicating that there has been no network
|
|
* activity */
|
|
/* for that period of time. The timeout is continued to determine */
|
|
/* whether or not this node may create a token. */
|
|
my_timeout = Tno_token + (Tslot * mstp_port->This_Station);
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) < my_timeout) {
|
|
if (mstp_port->EventCount > Nmin_octets) {
|
|
/* SawFrame */
|
|
/* Some other node exists at a lower address. */
|
|
/* Enter the IDLE state to receive and process the incoming
|
|
* frame. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
transition_now = true;
|
|
}
|
|
} else {
|
|
ns_timeout =
|
|
Tno_token + (Tslot * (mstp_port->This_Station + 1));
|
|
mm_timeout = Tno_token + (Tslot * (mstp_port->Nmax_master + 1));
|
|
if ((mstp_port->SilenceTimer((void *)mstp_port) < ns_timeout) ||
|
|
(mstp_port->SilenceTimer((void *)mstp_port) > mm_timeout)) {
|
|
/* GenerateToken */
|
|
/* Assume that this node is the lowest numerical address */
|
|
/* on the network and is empowered to create a token. */
|
|
mstp_port->Poll_Station = next_this_station;
|
|
/* Transmit a Poll For Master frame to PS. */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station, mstp_port->This_Station, NULL,
|
|
0);
|
|
/* indicate that the next station is unknown */
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->TokenCount = 0;
|
|
/* mstp_port->EventCount = 0;
|
|
removed Addendum 135-2004d-8 */
|
|
/* enter the POLL_FOR_MASTER state
|
|
to find a new successor to TS. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
|
|
} else {
|
|
/* We missed our time slot!
|
|
We should never get here unless
|
|
OS timer resolution is poor or we were busy */
|
|
if (mstp_port->EventCount > Nmin_octets) {
|
|
/* SawFrame */
|
|
/* Some other node exists at a lower address. */
|
|
/* Enter the IDLE state to receive and
|
|
process the incoming frame. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
transition_now = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_POLL_FOR_MASTER:
|
|
/* In the POLL_FOR_MASTER state, the node listens for a reply to */
|
|
/* a previously sent Poll For Master frame in order to find */
|
|
/* a successor node. */
|
|
if (mstp_port->ReceivedValidFrame == true) {
|
|
if ((mstp_port->DestinationAddress ==
|
|
mstp_port->This_Station) &&
|
|
(mstp_port->FrameType ==
|
|
FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) {
|
|
/* ReceivedReplyToPFM */
|
|
mstp_port->SoleMaster = false;
|
|
mstp_port->Next_Station = mstp_port->SourceAddress;
|
|
mstp_port->EventCount = 0;
|
|
/* Transmit a Token frame to NS */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TOKEN, mstp_port->Next_Station,
|
|
mstp_port->This_Station, NULL, 0);
|
|
mstp_port->Poll_Station = mstp_port->This_Station;
|
|
mstp_port->TokenCount = 0;
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
|
|
} else {
|
|
/* ReceivedUnexpectedFrame */
|
|
/* An unexpected frame was received. */
|
|
/* This may indicate the presence of multiple tokens. */
|
|
/* enter the IDLE state to synchronize with the network. */
|
|
/* This action drops the token. */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
transition_now = true;
|
|
}
|
|
mstp_port->ReceivedValidFrame = false;
|
|
} else if (
|
|
(mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Tusage_timeout) ||
|
|
(mstp_port->ReceivedInvalidFrame == true)) {
|
|
if (mstp_port->SoleMaster == true) {
|
|
/* SoleMaster */
|
|
/* There was no valid reply to the periodic poll */
|
|
/* by the sole known master for other masters. */
|
|
mstp_port->FrameCount = 0;
|
|
/* mstp_port->TokenCount++; removed in 2004 */
|
|
mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
|
|
transition_now = true;
|
|
} else {
|
|
if (mstp_port->Next_Station != mstp_port->This_Station) {
|
|
/* DoneWithPFM */
|
|
/* There was no valid reply to the maintenance */
|
|
/* poll for a master at address PS. */
|
|
mstp_port->EventCount = 0;
|
|
/* transmit a Token frame to NS */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TOKEN,
|
|
mstp_port->Next_Station, mstp_port->This_Station,
|
|
NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
|
|
} else {
|
|
if (next_poll_station != mstp_port->This_Station) {
|
|
/* SendNextPFM */
|
|
mstp_port->Poll_Station = next_poll_station;
|
|
/* Transmit a Poll For Master frame to PS. */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_POLL_FOR_MASTER,
|
|
mstp_port->Poll_Station,
|
|
mstp_port->This_Station, NULL, 0);
|
|
mstp_port->RetryCount = 0;
|
|
/* Re-enter the current state. */
|
|
} else {
|
|
/* DeclareSoleMaster */
|
|
/* to indicate that this station is the only master
|
|
*/
|
|
mstp_port->SoleMaster = true;
|
|
mstp_port->FrameCount = 0;
|
|
mstp_port->master_state =
|
|
MSTP_MASTER_STATE_USE_TOKEN;
|
|
transition_now = true;
|
|
}
|
|
}
|
|
}
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
}
|
|
break;
|
|
case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST:
|
|
/* The ANSWER_DATA_REQUEST state is entered when a */
|
|
/* BACnet Data Expecting Reply, a Test_Request, or */
|
|
/* a proprietary frame that expects a reply is received. */
|
|
/* FIXME: MSTP_Get_Reply waits for a matching reply, but
|
|
if the next queued message doesn't match, then we
|
|
sit here for Treply_delay doing nothing */
|
|
length = (unsigned)MSTP_Get_Reply(mstp_port, 0);
|
|
if (length > 0) {
|
|
/* Reply */
|
|
/* If a reply is available from the higher layers */
|
|
/* within Treply_delay after the reception of the */
|
|
/* final octet of the requesting frame */
|
|
/* (the mechanism used to determine this is a local matter), */
|
|
/* then call MSTP_Create_And_Send_Frame to transmit the reply
|
|
* frame */
|
|
/* and enter the IDLE state to wait for the next frame. */
|
|
MSTP_Send_Frame(
|
|
mstp_port, &mstp_port->OutputBuffer[0], (uint16_t)length);
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
/* clear our flag we were holding for comparison */
|
|
mstp_port->ReceivedValidFrame = false;
|
|
} else if (
|
|
mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Treply_delay) {
|
|
/* DeferredReply */
|
|
/* If no reply will be available from the higher layers */
|
|
/* within Treply_delay after the reception of the */
|
|
/* final octet of the requesting frame (the mechanism */
|
|
/* used to determine this is a local matter), */
|
|
/* then an immediate reply is not possible. */
|
|
/* Any reply shall wait until this node receives the token. */
|
|
/* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed
|
|
* frame, */
|
|
/* and enter the IDLE state. */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_REPLY_POSTPONED,
|
|
mstp_port->SourceAddress, mstp_port->This_Station, NULL, 0);
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
/* clear our flag we were holding for comparison */
|
|
mstp_port->ReceivedValidFrame = false;
|
|
}
|
|
break;
|
|
default:
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
break;
|
|
}
|
|
if (mstp_port->master_state != master_state) {
|
|
/* change of state detected - so print the details for debugging */
|
|
printf_master(
|
|
"MSTP: TS=%02X[%02X] NS=%02X[%02X] PS=%02X[%02X] EC=%u TC=%u ST=%u "
|
|
"%s\n",
|
|
mstp_port->This_Station, next_this_station, mstp_port->Next_Station,
|
|
next_next_station, mstp_port->Poll_Station, next_poll_station,
|
|
mstp_port->EventCount, mstp_port->TokenCount,
|
|
mstp_port->SilenceTimer((void *)mstp_port),
|
|
mstptext_master_state(mstp_port->master_state));
|
|
}
|
|
|
|
return transition_now;
|
|
}
|
|
|
|
/**
|
|
* @brief Finite State Machine for the Slave Node process
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
void MSTP_Slave_Node_FSM(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
unsigned length = 0;
|
|
|
|
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
|
|
if (mstp_port->ReceivedInvalidFrame == true) {
|
|
/* ReceivedInvalidFrame */
|
|
/* invalid frame was received */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (mstp_port->ReceivedValidFrame) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
switch (mstp_port->FrameType) {
|
|
case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY:
|
|
case FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY:
|
|
if (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS) {
|
|
/* indicate successful reception to the higher layers */
|
|
(void)MSTP_Put_Receive(mstp_port);
|
|
}
|
|
break;
|
|
case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY:
|
|
case FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY:
|
|
if ((mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS) &&
|
|
(npdu_confirmed_service(
|
|
mstp_port->InputBuffer, mstp_port->DataLength))) {
|
|
/* quietly discard any Confirmed-Request-PDU,
|
|
whose destination address is a multicast or
|
|
broadcast address, received from the
|
|
network layer. */
|
|
} else {
|
|
/* ForUs */
|
|
/* indicate successful reception
|
|
to the higher layers */
|
|
(void)MSTP_Put_Receive(mstp_port);
|
|
}
|
|
break;
|
|
case FRAME_TYPE_TEST_REQUEST:
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TEST_RESPONSE,
|
|
mstp_port->SourceAddress, mstp_port->This_Station,
|
|
&mstp_port->InputBuffer[0], mstp_port->DataLength);
|
|
break;
|
|
case FRAME_TYPE_TOKEN:
|
|
case FRAME_TYPE_POLL_FOR_MASTER:
|
|
case FRAME_TYPE_TEST_RESPONSE:
|
|
default:
|
|
break;
|
|
}
|
|
} else {
|
|
/* The ANSWER_DATA_REQUEST state is entered when a */
|
|
/* BACnet Data Expecting Reply, a Test_Request, or */
|
|
/* a proprietary frame that expects a reply is received. */
|
|
length = (unsigned)MSTP_Get_Reply(mstp_port, 0);
|
|
if (length > 0) {
|
|
/* Reply */
|
|
/* If a reply is available from the higher layers */
|
|
/* within Treply_delay after the reception of the */
|
|
/* final octet of the requesting frame */
|
|
/* (the mechanism used to determine this is a local
|
|
* matter), */
|
|
/* then call MSTP_Create_And_Send_Frame to transmit the
|
|
* reply frame */
|
|
/* and enter the IDLE state to wait for the next frame.
|
|
*/
|
|
MSTP_Send_Frame(
|
|
mstp_port, &mstp_port->OutputBuffer[0], (uint16_t)length);
|
|
/* clear our flag we were holding for comparison */
|
|
mstp_port->ReceivedValidFrame = false;
|
|
} else if (
|
|
mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Treply_delay) {
|
|
/* If no reply will be available from the higher layers
|
|
within Treply_delay after the reception of the final
|
|
octet of the requesting frame (the mechanism used to
|
|
determine this is a local matter), then no reply is
|
|
possible. */
|
|
/* clear our flag we were holding for comparison */
|
|
mstp_port->ReceivedValidFrame = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Initialize a UUID for storing the unique identifier of this node
|
|
* which is used to send and validate a test request and test response
|
|
* @note A Universally Unique IDentifier (UUID) - also called a
|
|
* Global Unique IDentifier (GUID) - is a 128-bit value.
|
|
*
|
|
* 4.4. Algorithms for Creating a UUID from Truly Random or
|
|
* Pseudo-Random Numbers
|
|
*
|
|
* The version 4 UUID is meant for generating UUIDs from truly-random or
|
|
* pseudo-random numbers.
|
|
*
|
|
* The algorithm is as follows:
|
|
*
|
|
* o Set the two most significant bits (bits 6 and 7) of the
|
|
* clock_seq_hi_and_reserved to zero and one, respectively.
|
|
*
|
|
* o Set the four most significant bits (bits 12 through 15) of the
|
|
* time_hi_and_version field to the 4-bit version number from
|
|
* Section 4.1.3.
|
|
*
|
|
* o Set all the other bits to randomly (or pseudo-randomly) chosen
|
|
* values.
|
|
*
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
unsigned i = 0;
|
|
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
/* 1. Generate 16 random bytes = 128 bits */
|
|
for (i = 0; i < MSTP_UUID_SIZE; i++) {
|
|
mstp_port->UUID[i] = rand() % 255;
|
|
}
|
|
/* 2. Adjust certain bits according to RFC 4122 section 4.4.
|
|
This just means do the following
|
|
(a) set the high nibble of the 7th byte equal to 4 and
|
|
(b) set the two most significant bits of the 9th byte to 10'B,
|
|
so the high nibble will be one of {8,9,A,B}.
|
|
From http://www.cryptosys.net/pki/Uuid.c.html */
|
|
mstp_port->UUID[6] = 0x40 | (mstp_port->UUID[6] & 0x0f);
|
|
mstp_port->UUID[8] = 0x80 | (mstp_port->UUID[8] & 0x3f);
|
|
}
|
|
|
|
/**
|
|
* @brief Increment the Zero Configuration Station address
|
|
* @param station the current station address in the range of min..max
|
|
* @return the next station address
|
|
*/
|
|
unsigned MSTP_Zero_Config_Station_Increment(unsigned station)
|
|
{
|
|
unsigned next_station;
|
|
|
|
if (station < Nmin_poll_station) {
|
|
next_station = Nmin_poll_station;
|
|
} else {
|
|
#ifdef MSTP_ZERO_CONFIG_STATION_INCREMENT_MODULO
|
|
/* as defined by specification language */
|
|
next_station = Nmin_poll_station +
|
|
((station + 1) % ((Nmax_poll_station - Nmin_poll_station) + 1));
|
|
#else
|
|
next_station = station + 1;
|
|
if (next_station > Nmax_poll_station) {
|
|
next_station = Nmin_poll_station;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
return next_station;
|
|
}
|
|
|
|
/**
|
|
* @brief The ZERO_CONFIGURATION_INIT state is entered when
|
|
* ZeroConfigurationMode is TRUE
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
static void MSTP_Zero_Config_State_Init(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
uint32_t slots;
|
|
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
mstp_port->Poll_Count = 0;
|
|
/* initialize the zero config station address */
|
|
if ((mstp_port->Zero_Config_Preferred_Station < Nmin_poll_station) ||
|
|
(mstp_port->Zero_Config_Preferred_Station > Nmax_poll_station)) {
|
|
mstp_port->Zero_Config_Preferred_Station = Nmin_poll_station;
|
|
}
|
|
mstp_port->Zero_Config_Station = mstp_port->Zero_Config_Preferred_Station;
|
|
mstp_port->Npoll_slot = 1 + (mstp_port->UUID[0] % Nmax_poll_slot);
|
|
/* basic silence timeout is the dropped token time plus
|
|
one Tslot after the last master node. Add one Tslot of
|
|
silence timeout per zero config priority slot */
|
|
slots = 128 + mstp_port->Npoll_slot;
|
|
mstp_port->Zero_Config_Silence = Tno_token + Tslot * slots;
|
|
mstp_port->Zero_Config_Max_Master = 0;
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
|
}
|
|
|
|
/**
|
|
* @brief The ZERO_CONFIGURATION_IDLE state is entered when
|
|
* ZeroConfigurationMode is TRUE, and a node is
|
|
* is waiting for any frame or waiting to timeout.
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
static void MSTP_Zero_Config_State_Idle(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
if (mstp_port->ReceivedValidFrame) {
|
|
/* next state will clear the frame flags */
|
|
/* MonitorPFM */
|
|
mstp_port->Poll_Count = 0;
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
|
} else if (mstp_port->ReceivedInvalidFrame) {
|
|
/* InvalidFrame */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (mstp_port->Zero_Config_Silence > 0) {
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Zero_Config_Silence) {
|
|
/* IdleTimeout */
|
|
/* long silence indicates we are alone or
|
|
with other silent devices */
|
|
/* claim the token at the current zero-config address */
|
|
/* configure max master at maximum */
|
|
/* confirm this station with a quick test */
|
|
mstp_port->Zero_Config_Max_Master = DEFAULT_MAX_MASTER;
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TEST_REQUEST,
|
|
mstp_port->Zero_Config_Station, mstp_port->Zero_Config_Station,
|
|
mstp_port->UUID, sizeof(mstp_port->UUID));
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CONFIRM;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief The ZERO_CONFIGURATION_LURK state is entered when
|
|
* ZeroConfigurationMode is TRUE, and a node is
|
|
* counting a Poll For Master frames to Zero_Config_Station address
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
static void MSTP_Zero_Config_State_Lurk(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
uint8_t count, frame, src, dst;
|
|
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
if (mstp_port->ReceivedValidFrame) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
dst = mstp_port->DestinationAddress;
|
|
src = mstp_port->SourceAddress;
|
|
frame = mstp_port->FrameType;
|
|
if (frame == FRAME_TYPE_POLL_FOR_MASTER) {
|
|
if ((dst > mstp_port->Zero_Config_Max_Master) &&
|
|
(dst <= DEFAULT_MAX_MASTER)) {
|
|
/* LearnMaxMaster */
|
|
mstp_port->Zero_Config_Max_Master = dst;
|
|
}
|
|
}
|
|
if (src == mstp_port->Zero_Config_Station) {
|
|
/* AddressInUse */
|
|
/* monitor PFM from the next address */
|
|
mstp_port->Zero_Config_Station = MSTP_Zero_Config_Station_Increment(
|
|
mstp_port->Zero_Config_Station);
|
|
mstp_port->Poll_Count = 0;
|
|
} else if (
|
|
(frame == FRAME_TYPE_POLL_FOR_MASTER) &&
|
|
(dst == mstp_port->Zero_Config_Station)) {
|
|
/* calculate this node poll count priority number */
|
|
count = Nmin_poll + mstp_port->Npoll_slot;
|
|
if (mstp_port->Poll_Count == count) {
|
|
/* PollResponse */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, src,
|
|
mstp_port->Zero_Config_Station, NULL, 0);
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CLAIM;
|
|
} else {
|
|
/* CountFrame */
|
|
mstp_port->Poll_Count++;
|
|
}
|
|
}
|
|
} else if (mstp_port->ReceivedInvalidFrame) {
|
|
/* InvalidFrame */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (mstp_port->Zero_Config_Silence > 0) {
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Zero_Config_Silence) {
|
|
/* LurkingTimeout */
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief The ZERO_CONFIGURATION_CLAIM state is entered when a node
|
|
* is waiting for a Token frame from the master to which it
|
|
* previously sent a Reply To Poll For Master frame, and
|
|
* ZeroConfigurationMode is TRUE.
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
static void MSTP_Zero_Config_State_Claim(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
uint8_t frame, src, dst;
|
|
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
if (mstp_port->ReceivedValidFrame) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
dst = mstp_port->DestinationAddress;
|
|
src = mstp_port->SourceAddress;
|
|
frame = mstp_port->FrameType;
|
|
if (src == mstp_port->Zero_Config_Station) {
|
|
/* ClaimAddressInUse */
|
|
/* monitor PFM from the next address */
|
|
mstp_port->Zero_Config_Station = MSTP_Zero_Config_Station_Increment(
|
|
mstp_port->Zero_Config_Station);
|
|
mstp_port->Poll_Count = 0;
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
|
} else if (frame == FRAME_TYPE_TOKEN) {
|
|
if (dst == mstp_port->Zero_Config_Station) {
|
|
/* ClaimTokenForUs */
|
|
MSTP_Create_And_Send_Frame(
|
|
mstp_port, FRAME_TYPE_TEST_REQUEST, src,
|
|
mstp_port->Zero_Config_Station, mstp_port->UUID,
|
|
MSTP_UUID_SIZE);
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_CONFIRM;
|
|
}
|
|
}
|
|
} else if (mstp_port->ReceivedInvalidFrame) {
|
|
/* ClaimInvalidFrame */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (mstp_port->Zero_Config_Silence > 0) {
|
|
/* ClaimTimeout */
|
|
if (mstp_port->SilenceTimer((void *)mstp_port) >
|
|
mstp_port->Zero_Config_Silence) {
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief The ZERO_CONFIGURATION_CONFIRM state is entered when
|
|
* a node is waiting for a Test Response frame and
|
|
* ZeroConfigurationMode is TRUE.
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
static void MSTP_Zero_Config_State_Confirm(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
bool match = false;
|
|
uint8_t frame, src, dst;
|
|
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
if (mstp_port->ReceivedValidFrame) {
|
|
mstp_port->ReceivedValidFrame = false;
|
|
dst = mstp_port->DestinationAddress;
|
|
src = mstp_port->SourceAddress;
|
|
frame = mstp_port->FrameType;
|
|
/* note: test frame could be from us. Check frame type first. */
|
|
if (frame == FRAME_TYPE_TEST_RESPONSE) {
|
|
if (dst == mstp_port->Zero_Config_Station) {
|
|
match = true;
|
|
}
|
|
if (match & (mstp_port->DataLength < MSTP_UUID_SIZE)) {
|
|
match = false;
|
|
}
|
|
if (match &&
|
|
(memcmp(
|
|
mstp_port->InputBuffer, mstp_port->UUID, MSTP_UUID_SIZE) !=
|
|
0)) {
|
|
match = false;
|
|
}
|
|
if (match) {
|
|
/* ConfirmationSuccessful */
|
|
mstp_port->This_Station = mstp_port->Zero_Config_Station;
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_USE;
|
|
} else {
|
|
/* ConfirmationFailed */
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_IDLE;
|
|
}
|
|
} else if (src == mstp_port->Zero_Config_Station) {
|
|
/* ConfirmationAddressInUse */
|
|
/* monitor PFM from the next address */
|
|
mstp_port->Zero_Config_Station = MSTP_Zero_Config_Station_Increment(
|
|
mstp_port->Zero_Config_Station);
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK;
|
|
}
|
|
} else if (mstp_port->ReceivedInvalidFrame) {
|
|
/* ConfirmationInvalidFrame */
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
} else if (
|
|
mstp_port->SilenceTimer((void *)mstp_port) >=
|
|
mstp_port->Treply_timeout) {
|
|
/* ConfirmationTimeout */
|
|
/* In case validating device doesn't support Test Request */
|
|
/* no response and no collision */
|
|
mstp_port->This_Station = mstp_port->Zero_Config_Station;
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_USE;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Finite State Machine for the Zero Configuration process
|
|
* @param mstp_port the context of the MSTP port
|
|
*/
|
|
void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
if (!mstp_port) {
|
|
return;
|
|
}
|
|
if (!mstp_port->ZeroConfigEnabled) {
|
|
return;
|
|
}
|
|
switch (mstp_port->Zero_Config_State) {
|
|
case MSTP_ZERO_CONFIG_STATE_INIT:
|
|
MSTP_Zero_Config_State_Init(mstp_port);
|
|
break;
|
|
case MSTP_ZERO_CONFIG_STATE_IDLE:
|
|
MSTP_Zero_Config_State_Idle(mstp_port);
|
|
break;
|
|
case MSTP_ZERO_CONFIG_STATE_LURK:
|
|
MSTP_Zero_Config_State_Lurk(mstp_port);
|
|
break;
|
|
case MSTP_ZERO_CONFIG_STATE_CLAIM:
|
|
MSTP_Zero_Config_State_Claim(mstp_port);
|
|
break;
|
|
case MSTP_ZERO_CONFIG_STATE_CONFIRM:
|
|
MSTP_Zero_Config_State_Confirm(mstp_port);
|
|
break;
|
|
case MSTP_ZERO_CONFIG_STATE_USE:
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* note: This_Station assumed to be set with the MAC address */
|
|
/* note: Nmax_info_frames assumed to be set (default=1) */
|
|
/* note: Nmax_master assumed to be set (default=127) */
|
|
/* note: InputBuffer and InputBufferSize assumed to be set */
|
|
/* note: OutputBuffer and OutputBufferSize assumed to be set */
|
|
/* note: SilenceTimer and SilenceTimerReset assumed to be set */
|
|
void MSTP_Init(struct mstp_port_struct_t *mstp_port)
|
|
{
|
|
if (mstp_port) {
|
|
#if 0
|
|
/* FIXME: you must point these buffers to actual byte buckets
|
|
in the dlmstp function before calling this init. */
|
|
mstp_port->InputBuffer = &InputBuffer[0];
|
|
mstp_port->InputBufferSize = sizeof(InputBuffer);
|
|
mstp_port->OutputBuffer = &OutputBuffer[0];
|
|
mstp_port->OutputBufferSize = sizeof(OutputBuffer);
|
|
/* FIXME: these are adjustable, so you must set these in dlmstp */
|
|
mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES;
|
|
mstp_port->Nmax_master = DEFAULT_MAX_MASTER;
|
|
mstp_port->Tframe_abort = DEFAULT_Tframe_abort;
|
|
mstp_port->Treply_delay = DEFAULT_Treply_delay;
|
|
mstp_port->Treply_timeout = DEFAULT_Treply_timeout;
|
|
mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout;
|
|
mstp_port->SlaveNodeEnabled = false;
|
|
/* FIXME: point to functions */
|
|
mstp_port->SilenceTimer = Timer_Silence;
|
|
mstp_port->SilenceTimerReset = Timer_Silence_Reset;
|
|
/* FIXME: set these in your dlmstp if you are zero-config */
|
|
mstp_port->ZeroConfigEnabled = true;
|
|
/* use the libc srand() and rand() generated random number*/
|
|
MSTP_Zero_Config_UUID_Init(&MSTP_Port);
|
|
#endif
|
|
if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) {
|
|
mstp_port->Tframe_abort = DEFAULT_Tframe_abort;
|
|
}
|
|
if ((mstp_port->Treply_delay == 0) || mstp_port->Treply_delay > 250) {
|
|
mstp_port->Treply_delay = DEFAULT_Treply_delay;
|
|
}
|
|
if ((mstp_port->Treply_timeout < 20) ||
|
|
(mstp_port->Treply_timeout > 300)) {
|
|
mstp_port->Treply_timeout = DEFAULT_Treply_timeout;
|
|
}
|
|
if ((mstp_port->Tusage_timeout < 20) ||
|
|
(mstp_port->Tusage_timeout > 35)) {
|
|
mstp_port->Tusage_timeout = DEFAULT_Tusage_timeout;
|
|
}
|
|
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
|
|
mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
|
|
mstp_port->ReceiveError = false;
|
|
mstp_port->DataAvailable = false;
|
|
mstp_port->DataRegister = 0;
|
|
mstp_port->DataCRC = 0;
|
|
mstp_port->DataLength = 0;
|
|
mstp_port->DestinationAddress = 0;
|
|
mstp_port->EventCount = 0;
|
|
mstp_port->FrameType = FRAME_TYPE_TOKEN;
|
|
mstp_port->FrameCount = 0;
|
|
mstp_port->HeaderCRC = 0;
|
|
mstp_port->Index = 0;
|
|
mstp_port->Next_Station = mstp_port->This_Station;
|
|
mstp_port->Poll_Station = mstp_port->This_Station;
|
|
mstp_port->ReceivedInvalidFrame = false;
|
|
mstp_port->ReceivedValidFrame = false;
|
|
mstp_port->RetryCount = 0;
|
|
mstp_port->SilenceTimerReset(mstp_port);
|
|
mstp_port->SoleMaster = false;
|
|
mstp_port->SourceAddress = 0;
|
|
mstp_port->TokenCount = 0;
|
|
/* zero config */
|
|
mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_INIT;
|
|
}
|
|
}
|