/************************************************************************** * * Copyright (C) 2010 Steve Karg * * SPDX-License-Identifier: GPL-2.0-or-later WITH GCC-exception-2.0 * *********************************************************************/ #include #include #include #include /* BACnet Stack defines - first */ #include "bacnet/bacdef.h" /* BACnet Stack API */ #include "bacnet/datalink/dlmstp.h" #include "bacnet/datalink/mstpdef.h" #include "bacnet/datalink/crc.h" #include "bacnet/npdu.h" #include "bacnet/bacaddr.h" #include "bacnet/basic/sys/ringbuf.h" #include "bacnet/basic/sys/mstimer.h" /* port specific */ #include "rs485.h" /* This file has been customized for use with small microprocessors */ /* Assumptions: Only one MS/TP datalink layer */ /* count must be a power of 2 for ringbuf library */ #ifndef MSTP_PDU_PACKET_COUNT #error MSTP_PDU_PACKET_COUNT must be defined! #endif /* The state of the Receive State Machine */ static MSTP_RECEIVE_STATE Receive_State; /* When a master node is powered up or reset, */ /* it shall unconditionally enter the INITIALIZE state. */ static MSTP_MASTER_STATE Master_State; /* bit-sized boolean flags */ static struct mstp_flag_t { /* A Boolean flag set to TRUE by the Receive State Machine */ /* if an invalid frame is received. */ /* Set to FALSE by the main state machine. */ unsigned ReceivedInvalidFrame : 1; /* A Boolean flag set to TRUE by the Receive State Machine */ /* if a valid frame is received. */ /* Set to FALSE by the main state machine. */ unsigned ReceivedValidFrame : 1; /* set to TRUE when we get a frame not for us */ unsigned ReceivedValidFrameNotForUs : 1; /* A Boolean flag set to TRUE by the master machine if this node is the */ /* only known master node. */ unsigned SoleMaster : 1; /* A Boolean flag set TRUE by the datalink if a packet has been received, but not processed. */ unsigned ReceivePacketPending : 1; } MSTP_Flag; /* Used to store the data length of a received frame. */ static uint32_t DataLength; /* Used to store the destination address of a received frame. */ static uint8_t DestinationAddress; /* Used to count the number of received octets or errors. */ /* This is used in the detection of link activity. */ /* Compared to Nmin_octets */ static uint8_t EventCount; /* Used to store the frame type of a received frame. */ static uint8_t FrameType; /* An array of octets, used to store octets as they are received. */ /* InputBuffer is indexed from 0 to InputBufferSize-1. */ /* FIXME: assign this to an actual array of bytes! */ /* Note: the buffer is designed as a pointer since some compilers and microcontroller architectures have limits as to places to hold contiguous memory. */ static uint8_t *InputBuffer; static uint16_t InputBufferSize; /* Used to store the Source Address of a received frame. */ static uint8_t SourceAddress; /* "This Station," the MAC address of this node. TS is generally read from a */ /* hardware DIP switch, or from nonvolatile memory. Valid values for TS are */ /* 0 to 254. The value 255 is used to denote broadcast when used as a */ /* destination address but is not allowed as a value for TS. */ static uint8_t This_Station; /* This parameter represents the value of the Max_Info_Frames property of */ /* the node's Device object. The value of Max_Info_Frames specifies the */ /* maximum number of information frames the node may send before it must */ /* pass the token. Max_Info_Frames may have different values on different */ /* nodes. This may be used to allocate more or less of the available link */ /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ /* node, its value shall be 1. */ static uint8_t Nmax_info_frames = MSTP_PDU_PACKET_COUNT; /* This parameter represents the value of the Max_Master property of the */ /* node's Device object. The value of Max_Master specifies the highest */ /* allowable address for master nodes. The value of Max_Master shall be */ /* less than or equal to 127. If Max_Master is not writable in a node, */ /* its value shall be 127. */ static uint8_t Nmax_master = 127; /* The minimum time without a DataAvailable or ReceiveError event */ /* that a node must wait for a station to begin replying to a */ /* confirmed request: 255 milliseconds. (Implementations may use */ /* larger values for this timeout, not to exceed 300 milliseconds.) */ #ifndef Treply_timeout #define Treply_timeout 260 #endif /* The time without a DataAvailable or ReceiveError event that a node must */ /* wait for a remote node to begin using a token or replying to a Poll For */ /* Master frame: 20 milliseconds. (Implementations may use larger values for */ /* this timeout, not to exceed 35 milliseconds.) */ #ifndef Tusage_timeout #define Tusage_timeout 30 #endif /* The number of tokens received or used before a Poll For Master cycle */ /* is executed: 50. */ #define Npoll 50 /* The number of retries on sending Token: 1. */ #define Nretry_token 1 /* The minimum number of DataAvailable or ReceiveError events that must be */ /* seen by a receiving node in order to declare the line "active": 4. */ #define Nmin_octets 4 /* The minimum time without a DataAvailable or ReceiveError event within */ /* a frame before a receiving node may discard the frame: 60 bit times. */ /* (Implementations may use larger values for this timeout, */ /* not to exceed 100 milliseconds.) */ /* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ /* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ #ifndef Tframe_abort #define Tframe_abort 30 #endif /* The maximum time a node may wait after reception of a frame that expects */ /* a reply before sending the first octet of a reply or Reply Postponed */ /* frame: 250 milliseconds. */ #ifndef Treply_delay #define Treply_delay 250 #endif /* we need to be able to increment without rolling over */ #define INCREMENT_AND_LIMIT_UINT8(x) \ { \ if (x < 0xFF) \ x++; \ } /* data structure for MS/TP PDU Queue */ struct mstp_pdu_packet { bool data_expecting_reply; uint8_t destination_mac; uint16_t length; uint8_t buffer[DLMSTP_MPDU_MAX]; }; static struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT]; static RING_BUFFER PDU_Queue; bool dlmstp_init(char *ifname) { ifname = ifname; Ringbuf_Init(&PDU_Queue, (uint8_t *)&PDU_Buffer, sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT); return true; } void dlmstp_cleanup(void) { /* nothing to do for static buffers */ } void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) { int i = 0; 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; } } static bool dlmstp_compare_data_expecting_reply(const uint8_t *request_pdu, uint16_t request_pdu_len, uint8_t src_address, const uint8_t *reply_pdu, uint16_t reply_pdu_len, uint8_t dest_address) { uint16_t offset; /* One way to check the message is to compare NPDU src, dest, along with the APDU type, invoke id. Seems a bit overkill */ struct DER_compare_t { BACNET_NPDU_DATA npdu_data; BACNET_ADDRESS address; uint8_t pdu_type; uint8_t invoke_id; uint8_t service_choice; }; struct DER_compare_t request; struct DER_compare_t reply; /* decode the request data */ request.address.mac[0] = src_address; request.address.mac_len = 1; offset = bacnet_npdu_decode(request_pdu, request_pdu_len, NULL, &request.address, &request.npdu_data); if (request.npdu_data.network_layer_message) { return false; } request.pdu_type = request_pdu[offset] & 0xF0; if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { return false; } request.invoke_id = request_pdu[offset + 2]; /* segmented message? */ if (request_pdu[offset] & BIT(3)) request.service_choice = request_pdu[offset + 5]; else request.service_choice = request_pdu[offset + 3]; /* decode the reply data */ reply.address.mac[0] = dest_address; reply.address.mac_len = 1; offset = bacnet_npdu_decode( reply_pdu, reply_pdu_len, &reply.address, NULL, &reply.npdu_data); if (reply.npdu_data.network_layer_message) { return false; } /* reply could be a lot of things: confirmed, simple ack, abort, reject, error */ reply.pdu_type = reply_pdu[offset] & 0xF0; switch (reply.pdu_type) { case PDU_TYPE_SIMPLE_ACK: reply.invoke_id = reply_pdu[offset + 1]; reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_COMPLEX_ACK: reply.invoke_id = reply_pdu[offset + 1]; /* segmented message? */ if (reply_pdu[offset] & BIT(3)) reply.service_choice = reply_pdu[offset + 4]; else reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_ERROR: reply.invoke_id = reply_pdu[offset + 1]; reply.service_choice = reply_pdu[offset + 2]; break; case PDU_TYPE_REJECT: case PDU_TYPE_ABORT: reply.invoke_id = reply_pdu[offset + 1]; break; default: return false; } if (request.invoke_id != reply.invoke_id) { return false; } /* these services don't have service choice included */ if ((reply.pdu_type != PDU_TYPE_REJECT) && (reply.pdu_type != PDU_TYPE_ABORT)) { if (request.service_choice != reply.service_choice) { return false; } } if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { return false; } #if 0 /* the NDPU priority doesn't get passed through the stack, and all outgoing messages have NORMAL priority */ if (request.npdu_data.priority != reply.npdu_data.priority) { return false; } #endif if (!bacnet_address_same(&request.address, &reply.address)) { return false; } return true; } /* 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' */ static void MSTP_Send_Frame( uint8_t frame_type, /* type of frame to send - see defines */ uint8_t destination, /* destination address */ uint8_t source, /* source address */ const uint8_t *data, /* any data to be sent - may be null */ uint16_t data_len) { /* number of bytes of data (up to 501) */ uint8_t crc8 = 0xFF; /* used to calculate the crc value */ uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ uint8_t buffer[8]; /* stores the header and header crc */ uint8_t buffer_crc[2]; /* stores the data crc */ uint16_t i = 0; /* used to calculate CRC for data */ /* create the MS/TP header */ 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] = HI_BYTE(data_len); crc8 = CRC_Calc_Header(buffer[5], crc8); buffer[6] = LO_BYTE(data_len); crc8 = CRC_Calc_Header(buffer[6], crc8); buffer[7] = ~crc8; if (data_len) { /* calculate CRC for any data */ for (i = 0; i < data_len; i++) { crc16 = CRC_Calc_Data(data[i], crc16); } crc16 = ~crc16; buffer_crc[0] = (crc16 & 0x00FF); buffer_crc[1] = ((crc16 & 0xFF00) >> 8); } /* on a slower processor, we don't want to calculate the CRC after we send the header because there will be a gap */ rs485_turnaround_delay(); rs485_rts_enable(true); rs485_bytes_send(buffer, 8); if (data_len) { rs485_bytes_send(data, data_len); rs485_bytes_send(buffer_crc, 2); } rs485_rts_enable(false); } static void MSTP_Receive_Frame_FSM(void) { /* stores the latest received data octet */ uint8_t DataRegister = 0; /* Used to accumulate the CRC on the data field of a frame. */ static uint16_t DataCRC = 0; /* Used to accumulate the CRC on the header of a frame. */ static uint8_t HeaderCRC = 0; /* Used as an index by the Receive State Machine, up to a maximum value of the MPDU */ static uint16_t Index = 0; switch (Receive_State) { case MSTP_RECEIVE_STATE_IDLE: /* In the IDLE state, the node waits for the beginning of a frame. */ if (rs485_receive_error()) { /* EatAnError */ rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); } else if (rs485_byte_available(&DataRegister)) { rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); if (DataRegister == 0x55) { /* Preamble1 */ /* receive the remainder of the frame. */ Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; } } break; case MSTP_RECEIVE_STATE_PREAMBLE: /* In the PREAMBLE state, the node waits for the second octet of the preamble. */ if (rs485_silence_time_elapsed(Tframe_abort)) { /* Timeout */ /* a correct preamble has not been received */ /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_receive_error()) { /* Error */ rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_byte_available(&DataRegister)) { rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); if (DataRegister == 0xFF) { /* Preamble2 */ Index = 0; HeaderCRC = 0xFF; /* receive the remainder of the frame. */ Receive_State = MSTP_RECEIVE_STATE_HEADER; } else if (DataRegister == 0x55) { /* ignore RepeatedPreamble1 */ /* wait for the second preamble octet. */ Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; } else { /* NotPreamble */ /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } } break; case MSTP_RECEIVE_STATE_HEADER: /* In the HEADER state, the node waits for the fixed message header. */ if (rs485_silence_time_elapsed(Tframe_abort)) { /* Timeout */ /* indicate that an error has occurred during the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_receive_error()) { /* Error */ rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); /* indicate that an error has occurred during the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_byte_available(&DataRegister)) { rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); if (Index == 0) { /* FrameType */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); FrameType = DataRegister; Index = 1; } else if (Index == 1) { /* Destination */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); DestinationAddress = DataRegister; Index = 2; } else if (Index == 2) { /* Source */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); SourceAddress = DataRegister; Index = 3; } else if (Index == 3) { /* Length1 */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); DataLength = DataRegister * 256; Index = 4; } else if (Index == 4) { /* Length2 */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); DataLength += DataRegister; Index = 5; } else if (Index == 5) { /* HeaderCRC */ HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); /* In the HEADER_CRC state, the node validates the CRC on the fixed message header. */ if (HeaderCRC != 0x55) { /* BadCRC */ /* indicate that an error has occurred during the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of the next frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else { if (DataLength == 0) { /* NoData */ if ((DestinationAddress == This_Station) || (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { /* ForUs */ /* indicate that a frame with no data has been received */ MSTP_Flag.ReceivedValidFrame = true; } else { /* NotForUs */ MSTP_Flag.ReceivedValidFrameNotForUs = true; } /* wait for the start of the next frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else { /* receive the data portion of the frame. */ if ((DestinationAddress == This_Station) || (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { if (DataLength <= InputBufferSize) { /* Data */ Receive_State = MSTP_RECEIVE_STATE_DATA; } else { /* FrameTooLong */ Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; } } else { /* NotForUs */ Receive_State = MSTP_RECEIVE_STATE_SKIP_DATA; } Index = 0; DataCRC = 0xFFFF; } } } else { /* indicate that an error has occurred during */ /* the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of a frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } } break; case MSTP_RECEIVE_STATE_DATA: case MSTP_RECEIVE_STATE_SKIP_DATA: /* In the DATA state, the node waits for the data portion of a frame. */ if (rs485_silence_time_elapsed(Tframe_abort)) { /* Timeout */ /* indicate that an error has occurred during the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of the next frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_receive_error()) { /* Error */ rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); /* indicate that an error has occurred during the reception of a frame */ MSTP_Flag.ReceivedInvalidFrame = true; /* wait for the start of the next frame. */ Receive_State = MSTP_RECEIVE_STATE_IDLE; } else if (rs485_byte_available(&DataRegister)) { rs485_silence_time_reset(); INCREMENT_AND_LIMIT_UINT8(EventCount); if (Index < DataLength) { /* DataOctet */ DataCRC = CRC_Calc_Data(DataRegister, DataCRC); if (Index < InputBufferSize) { InputBuffer[Index] = DataRegister; } Index++; } else if (Index == DataLength) { /* CRC1 */ DataCRC = CRC_Calc_Data(DataRegister, DataCRC); Index++; } else if (Index == (DataLength + 1)) { /* CRC2 */ DataCRC = CRC_Calc_Data(DataRegister, DataCRC); /* STATE DATA CRC - no need for new state */ /* indicate the complete reception of a valid frame */ if (DataCRC == 0xF0B8) { if (Receive_State == MSTP_RECEIVE_STATE_DATA) { /* ForUs */ MSTP_Flag.ReceivedValidFrame = true; } else { /* NotForUs */ MSTP_Flag.ReceivedValidFrameNotForUs = true; } } else { MSTP_Flag.ReceivedInvalidFrame = true; } Receive_State = MSTP_RECEIVE_STATE_IDLE; } } break; default: /* shouldn't get here - but if we do... */ Receive_State = MSTP_RECEIVE_STATE_IDLE; break; } return; } #ifdef MSTP_DEBUG_STATES static MSTP_MASTER_STATE Master_State_Log[128]; static unsigned master_state_log_index = 0; void log_master_state(MSTP_MASTER_STATE state) { Master_State_Log[master_state_log_index] = state; master_state_log_index++; if (master_state_log_index > 128) { master_state_log_index = 0; } } #else #define log_master_state(n) (void)n; #endif /* returns true if we need to transition immediately */ static bool MSTP_Master_Node_FSM(void) { /* The number of frames sent by this node during a single token hold. */ /* When this counter reaches the value Nmax_info_frames, the node must */ /* pass the token. */ static uint8_t FrameCount = 0; /* "Next Station," the MAC address of the node to which This Station passes the token. If the Next_Station is unknown, Next_Station shall be equal to This_Station. */ static uint8_t Next_Station = 0; /* "Poll Station," the MAC address of the node to which This Station last */ /* sent a Poll For Master. This is used during token maintenance. */ static uint8_t Poll_Station = 0; /* A counter of transmission retries used for Token and Poll For Master */ /* transmission. */ static unsigned RetryCount = 0; /* The number of tokens received by this node. When this counter reaches */ /* the value Npoll, the node polls the address range between TS and NS */ /* for additional master nodes. TokenCount is set to zero at the end of */ /* the polling process. */ static unsigned TokenCount = 0; /* next-x-station calculations */ uint8_t next_poll_station = 0; uint8_t next_this_station = 0; uint8_t next_next_station = 0; /* timeout values */ uint16_t my_timeout = 10, ns_timeout = 0; bool matched = false; bool timeout = false; /* transition immediately to the next state */ bool transition_now = false; /* packet from the PDU Queue */ struct mstp_pdu_packet *pkt = NULL; /* some calculations that several states need */ next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); next_this_station = (This_Station + 1) % (Nmax_master + 1); next_next_station = (Next_Station + 1) % (Nmax_master + 1); log_master_state(Master_State); switch (Master_State) { case MSTP_MASTER_STATE_INITIALIZE: /* DoneInitializing */ /* indicate that the next station is unknown */ Next_Station = This_Station; Poll_Station = This_Station; /* cause a Poll For Master to be sent when this node first */ /* receives the token */ TokenCount = Npoll; MSTP_Flag.SoleMaster = false; 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 (rs485_silence_time_elapsed(Tno_token)) { /* LostToken */ /* assume that the token has been lost */ EventCount = 0; /* Addendum 135-2004d-8 */ /* set the receive frame flags to false in case we received some bytes and had a timeout for some reason */ MSTP_Flag.ReceivedValidFrame = false; MSTP_Flag.ReceivedInvalidFrame = false; MSTP_Flag.ReceivedValidFrameNotForUs = false; Master_State = MSTP_MASTER_STATE_NO_TOKEN; transition_now = true; } else if (MSTP_Flag.ReceivedInvalidFrame == true) { /* ReceivedInvalidFrame */ /* invalid frame was received */ MSTP_Flag.ReceivedInvalidFrame = false; /* wait for the next frame - remain in IDLE */ } else if (MSTP_Flag.ReceivedValidFrame == true) { switch (FrameType) { case FRAME_TYPE_TOKEN: /* ReceivedToken */ /* tokens can't be broadcast */ if (DestinationAddress == MSTP_BROADCAST_ADDRESS) break; MSTP_Flag.ReceivedValidFrame = false; FrameCount = 0; MSTP_Flag.SoleMaster = false; Master_State = MSTP_MASTER_STATE_USE_TOKEN; transition_now = true; break; case FRAME_TYPE_POLL_FOR_MASTER: /* ReceivedPFM */ /* DestinationAddress is equal to TS */ if (DestinationAddress == This_Station) { MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, SourceAddress, This_Station, NULL, 0); } break; case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: if ((DestinationAddress == MSTP_BROADCAST_ADDRESS) && (npdu_confirmed_service(InputBuffer, 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 { /* indicate successful reception to higher layer */ MSTP_Flag.ReceivePacketPending = true; } break; case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: if (DestinationAddress == MSTP_BROADCAST_ADDRESS) { /* broadcast DER just remains IDLE */ } else { /* indicate successful reception to higher layers */ MSTP_Flag.ReceivePacketPending = true; Master_State = MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; } break; case FRAME_TYPE_TEST_REQUEST: MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress, This_Station, &InputBuffer[0], 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 (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { MSTP_Flag.ReceivedValidFrame = false; } } break; /* 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. */ case MSTP_MASTER_STATE_USE_TOKEN: /* Note: We could wait for up to Tusage_delay */ if (Ringbuf_Empty(&PDU_Queue)) { /* NothingToSend */ FrameCount = Nmax_info_frames; Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; transition_now = true; } else { uint8_t frame_type; pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue); if (pkt->data_expecting_reply) { frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; } else { frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; } MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, &pkt->buffer[0], pkt->length); FrameCount++; switch (frame_type) { case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: /* SendAndWait */ if (pkt->destination_mac == MSTP_BROADCAST_ADDRESS) Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; else Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; break; case FRAME_TYPE_TEST_REQUEST: Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; break; case FRAME_TYPE_TEST_RESPONSE: case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: default: /* SendNoWait */ Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; break; } (void)Ringbuf_Pop(&PDU_Queue, NULL); } break; case MSTP_MASTER_STATE_WAIT_FOR_REPLY: /* In the WAIT_FOR_REPLY state, the node waits for */ /* a reply from another node. */ if (rs485_silence_time_elapsed(Treply_timeout)) { /* ReplyTimeout */ /* assume that the request has failed */ FrameCount = Nmax_info_frames; 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_Flag.ReceivedInvalidFrame == true) { /* InvalidFrame */ /* error in frame reception */ MSTP_Flag.ReceivedInvalidFrame = false; Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; transition_now = true; } else if (MSTP_Flag.ReceivedValidFrame == true) { if (DestinationAddress == This_Station) { /* What did we receive? */ switch (FrameType) { case FRAME_TYPE_REPLY_POSTPONED: /* ReceivedReplyPostponed */ Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; break; case FRAME_TYPE_TEST_RESPONSE: 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 */ MSTP_Flag.ReceivePacketPending = true; Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; break; default: /* if proprietary frame was expected, you might need to transition to DONE WITH TOKEN */ Master_State = MSTP_MASTER_STATE_IDLE; break; } } else { /* ReceivedUnexpectedFrame */ /* an unexpected frame was received */ /* This may indicate the presence of multiple tokens */ /* or a device that didn't see activity after passing */ /* a token (how lame!). */ /* Synchronize with the network. */ /* This action drops the token. */ Master_State = MSTP_MASTER_STATE_IDLE; } MSTP_Flag.ReceivedValidFrame = false; transition_now = true; } } break; /* The DONE_WITH_TOKEN state either sends another data frame, */ /* passes the token, or initiates a Poll For Master cycle. */ case MSTP_MASTER_STATE_DONE_WITH_TOKEN: /* SendAnotherFrame */ if (FrameCount < Nmax_info_frames) { /* then this node may send another information frame */ /* before passing the token. */ Master_State = MSTP_MASTER_STATE_USE_TOKEN; transition_now = true; } else if ((MSTP_Flag.SoleMaster == false) && (Next_Station == This_Station)) { /* NextStationUnknown - added in Addendum 135-2008v-1 */ /* then the next station to which the token should be sent is unknown - so PollForMaster */ Poll_Station = next_this_station; MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); RetryCount = 0; Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; } /* Npoll changed in Errata SSPC-135-2004 */ else if (TokenCount < (Npoll - 1)) { if ((MSTP_Flag.SoleMaster == true) && (Next_Station != next_this_station)) { /* SoleMaster */ /* there are no other known master nodes to */ /* which the token may be sent (true master-slave operation). */ FrameCount = 0; TokenCount++; 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. */ TokenCount++; /* transmit a Token frame to NS */ MSTP_Send_Frame( FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0); RetryCount = 0; EventCount = 0; Master_State = MSTP_MASTER_STATE_PASS_TOKEN; } } else if (next_poll_station == Next_Station) { if (MSTP_Flag.SoleMaster == true) { /* SoleMasterRestartMaintenancePFM */ Poll_Station = next_next_station; MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); /* no known successor node */ Next_Station = This_Station; RetryCount = 0; TokenCount = 1; /* changed in Errata SSPC-135-2004 */ /* EventCount = 0; removed in Addendum 135-2004d-8 */ /* find a new successor to TS */ Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; } else { /* ResetMaintenancePFM */ Poll_Station = This_Station; /* transmit a Token frame to NS */ MSTP_Send_Frame( FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0); RetryCount = 0; TokenCount = 1; /* changed in Errata SSPC-135-2004 */ EventCount = 0; Master_State = MSTP_MASTER_STATE_PASS_TOKEN; } } else { /* SendMaintenancePFM */ Poll_Station = next_poll_station; MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); RetryCount = 0; Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; } break; /* The PASS_TOKEN state listens for a successor to begin using */ /* the token that this node has just attempted to pass. */ case MSTP_MASTER_STATE_PASS_TOKEN: if (rs485_silence_time_elapsed(Tusage_timeout)) { if (RetryCount < Nretry_token) { /* RetrySendToken */ RetryCount++; /* Transmit a Token frame to NS */ MSTP_Send_Frame( FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0); 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! */ Poll_Station = next_next_station; /* Transmit a Poll For Master frame to PS. */ MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); /* no known successor node */ Next_Station = This_Station; RetryCount = 0; TokenCount = 0; /* EventCount = 0; removed in Addendum 135-2004d-8 */ /* find a new successor to TS */ Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; } } else { if (EventCount > Nmin_octets) { /* SawTokenUser */ /* Assume that a frame has been sent by the new token user. */ /* Enter the IDLE state to process the frame. */ Master_State = MSTP_MASTER_STATE_IDLE; transition_now = true; } } break; /* The NO_TOKEN state is entered if Silence Timer 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. */ case MSTP_MASTER_STATE_NO_TOKEN: my_timeout = Tno_token + (Tslot * This_Station); if (rs485_silence_time_elapsed(my_timeout)) { ns_timeout = Tno_token + (Tslot * (This_Station + 1)); if (rs485_silence_time_elapsed(ns_timeout)) { /* should never get here unless timer resolution is bad */ rs485_silence_time_reset(); Master_State = MSTP_MASTER_STATE_IDLE; } else { /* GenerateToken */ /* Assume that this node is the lowest numerical address */ /* on the network and is empowered to create a token. */ Poll_Station = next_this_station; /* Transmit a Poll For Master frame to PS. */ MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); /* indicate that the next station is unknown */ Next_Station = This_Station; RetryCount = 0; TokenCount = 0; /* EventCount = 0; removed Addendum 135-2004d-8 */ /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; } } else { if (EventCount > Nmin_octets) { /* SawFrame */ /* Some other node exists at a lower address. */ /* Enter the IDLE state to receive and process the incoming frame. */ Master_State = MSTP_MASTER_STATE_IDLE; transition_now = true; } } break; /* 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. */ case MSTP_MASTER_STATE_POLL_FOR_MASTER: if (MSTP_Flag.ReceivedValidFrame == true) { if ((DestinationAddress == This_Station) && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { /* ReceivedReplyToPFM */ MSTP_Flag.SoleMaster = false; Next_Station = SourceAddress; EventCount = 0; /* Transmit a Token frame to NS */ MSTP_Send_Frame( FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0); Poll_Station = This_Station; TokenCount = 0; RetryCount = 0; 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. */ Master_State = MSTP_MASTER_STATE_IDLE; transition_now = true; } MSTP_Flag.ReceivedValidFrame = false; } else if ((rs485_silence_time_elapsed(Tusage_timeout)) || (MSTP_Flag.ReceivedInvalidFrame == true)) { if (MSTP_Flag.SoleMaster == true) { /* SoleMaster */ /* There was no valid reply to the periodic poll */ /* by the sole known master for other masters. */ FrameCount = 0; /* TokenCount++; removed in 2004 */ Master_State = MSTP_MASTER_STATE_USE_TOKEN; transition_now = true; } else { if (Next_Station != This_Station) { /* DoneWithPFM */ /* There was no valid reply to the maintenance */ /* poll for a master at address PS. */ EventCount = 0; /* transmit a Token frame to NS */ MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, This_Station, NULL, 0); RetryCount = 0; Master_State = MSTP_MASTER_STATE_PASS_TOKEN; } else { if (next_poll_station != This_Station) { /* SendNextPFM */ Poll_Station = next_poll_station; /* Transmit a Poll For Master frame to PS. */ MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, This_Station, NULL, 0); RetryCount = 0; /* Re-enter the current state. */ } else { /* DeclareSoleMaster */ /* to indicate that this station is the only master */ MSTP_Flag.SoleMaster = true; FrameCount = 0; Master_State = MSTP_MASTER_STATE_USE_TOKEN; transition_now = true; } } } MSTP_Flag.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. */ timeout = rs485_silence_time_elapsed(Treply_delay); if (!timeout) { pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue); if (pkt != NULL) { matched = dlmstp_compare_data_expecting_reply( &InputBuffer[0], DataLength, SourceAddress, &pkt->buffer[0], pkt->length, pkt->destination_mac); } else { matched = false; } } if (matched) { /* 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_Send_Frame to transmit the reply frame */ /* and enter the IDLE state to wait for the next frame. */ uint8_t frame_type; if (pkt->data_expecting_reply) { frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; } else { frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; } MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, &pkt->buffer[0], pkt->length); Master_State = MSTP_MASTER_STATE_IDLE; /* clear our flag we were holding for comparison */ MSTP_Flag.ReceivedValidFrame = false; /* clear the queue */ (void)Ringbuf_Pop(&PDU_Queue, NULL); } else if ((pkt != NULL) || timeout) { /* 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_Send_Frame to transmit a Reply Postponed frame, */ /* and enter the IDLE state. */ MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, This_Station, NULL, 0); Master_State = MSTP_MASTER_STATE_IDLE; /* clear our flag we were holding for comparison */ MSTP_Flag.ReceivedValidFrame = false; } break; default: Master_State = MSTP_MASTER_STATE_IDLE; break; } return transition_now; } static void MSTP_Slave_Node_FSM(void) { /* packet from the PDU Queue */ struct mstp_pdu_packet *pkt; /* did the frame in the queue match the last request? */ bool matched = false; Master_State = MSTP_MASTER_STATE_IDLE; if (MSTP_Flag.ReceivedInvalidFrame == true) { /* ReceivedInvalidFrame */ /* invalid frame was received */ MSTP_Flag.ReceivedInvalidFrame = false; } else if (MSTP_Flag.ReceivedValidFrame) { MSTP_Flag.ReceivedValidFrame = false; switch (FrameType) { case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { /* indicate successful reception to the higher layers */ MSTP_Flag.ReceivePacketPending = true; } break; case FRAME_TYPE_TEST_REQUEST: MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, SourceAddress, This_Station, &InputBuffer[0], DataLength); break; case FRAME_TYPE_TOKEN: case FRAME_TYPE_POLL_FOR_MASTER: case FRAME_TYPE_TEST_RESPONSE: case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: default: break; } } else if (MSTP_Flag.ReceivePacketPending) { if (!Ringbuf_Empty(&PDU_Queue)) { pkt = (struct mstp_pdu_packet *)Ringbuf_Peek(&PDU_Queue); matched = dlmstp_compare_data_expecting_reply(&InputBuffer[0], DataLength, SourceAddress, &pkt->buffer[0], pkt->length, pkt->destination_mac); if (matched) { /* 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_Send_Frame to transmit the reply frame */ /* and enter the IDLE state to wait for the next frame. */ uint8_t frame_type; if (pkt->data_expecting_reply) { frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; } else { frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; } MSTP_Send_Frame(frame_type, pkt->destination_mac, This_Station, &pkt->buffer[0], pkt->length); (void)Ringbuf_Pop(&PDU_Queue, NULL); } /* clear our flag we were holding for comparison */ MSTP_Flag.ReceivePacketPending = false; } else if (rs485_silence_time_elapsed(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_Flag.ReceivePacketPending = false; } } } /* returns number of bytes sent on success, zero on failure */ int dlmstp_send_pdu(BACNET_ADDRESS *dest, /* destination address */ BACNET_NPDU_DATA *npdu_data, /* network information */ uint8_t *pdu, /* any data to be sent - may be null */ unsigned pdu_len) { /* number of bytes of data */ int bytes_sent = 0; struct mstp_pdu_packet *pkt; uint16_t i = 0; pkt = (struct mstp_pdu_packet *)Ringbuf_Data_Peek(&PDU_Queue); if (pkt) { pkt->data_expecting_reply = npdu_data->data_expecting_reply; for (i = 0; i < pdu_len; i++) { pkt->buffer[i] = pdu[i]; } pkt->length = pdu_len; if (dest && dest->mac_len) { pkt->destination_mac = dest->mac[0]; } else { /* mac_len = 0 is a broadcast address */ pkt->destination_mac = MSTP_BROADCAST_ADDRESS; } if (Ringbuf_Data_Put(&PDU_Queue, (uint8_t *)pkt)) { bytes_sent = pdu_len; } } return bytes_sent; } /* Return the length of the packet */ uint16_t dlmstp_receive(BACNET_ADDRESS *src, /* source address */ uint8_t *pdu, /* PDU data */ uint16_t max_pdu, /* amount of space available in the PDU */ unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; /* return value */ /* set the input buffer to the same data storage for zero copy */ if (!InputBuffer) { InputBuffer = pdu; InputBufferSize = max_pdu; } while ((MSTP_Flag.ReceivedValidFrame == false) && (MSTP_Flag.ReceivedValidFrameNotForUs == false) && (MSTP_Flag.ReceivedInvalidFrame == false)) { /* only do receive state machine while we don't have a frame */ MSTP_Receive_Frame_FSM(); /* process another byte, if available */ if (!rs485_byte_available(NULL)) { break; } } if (MSTP_Flag.ReceivedValidFrameNotForUs) { MSTP_Flag.ReceivedValidFrameNotForUs = false; } if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { /* only do master or slave state machine while rx is idle */ if (This_Station <= DEFAULT_MAX_MASTER) { while (MSTP_Master_Node_FSM()) { /* do nothing while some states fast transition */ }; } else if (This_Station < 255) { MSTP_Slave_Node_FSM(); } #if SLEEP_ENABLED sleep_mode(); #endif } /* if there is a packet that needs processed, do it now. */ if (MSTP_Flag.ReceivePacketPending) { if (This_Station <= 127) { /* master nodes clear immediately */ MSTP_Flag.ReceivePacketPending = false; } pdu_len = DataLength; src->mac_len = 1; src->mac[0] = SourceAddress; /* data is already in the pdu pointer */ } return pdu_len; } void dlmstp_set_mac_address(uint8_t mac_address) { /* Master Nodes can only have address 0-127 */ if (mac_address <= 127) { This_Station = mac_address; if (mac_address > Nmax_master) dlmstp_set_max_master(127); } return; } uint8_t dlmstp_mac_address(void) { return This_Station; } /* This parameter represents the value of the Max_Info_Frames property of */ /* the node's Device object. The value of Max_Info_Frames specifies the */ /* maximum number of information frames the node may send before it must */ /* pass the token. Max_Info_Frames may have different values on different */ /* nodes. This may be used to allocate more or less of the available link */ /* bandwidth to particular nodes. If Max_Info_Frames is not writable in a */ /* node, its value shall be 1. */ void dlmstp_set_max_info_frames(uint8_t max_info_frames) { if (max_info_frames >= MSTP_PDU_PACKET_COUNT) { Nmax_info_frames = max_info_frames; } return; } uint8_t dlmstp_max_info_frames(void) { return Nmax_info_frames; } /* This parameter represents the value of the Max_Master property of the */ /* node's Device object. The value of Max_Master specifies the highest */ /* allowable address for master nodes. The value of Max_Master shall be */ /* less than or equal to 127. If Max_Master is not writable in a node, */ /* its value shall be 127. */ void dlmstp_set_max_master(uint8_t max_master) { if (max_master <= 127) { if (This_Station <= max_master) { Nmax_master = max_master; } } return; } uint8_t dlmstp_max_master(void) { return Nmax_master; } void dlmstp_get_my_address(BACNET_ADDRESS *my_address) { int i = 0; /* counter */ my_address->mac_len = 1; my_address->mac[0] = This_Station; my_address->net = 0; /* local only, no routing */ my_address->len = 0; for (i = 0; i < MAX_MAC_LEN; i++) { my_address->adr[i] = 0; } return; } void dlmstp_get_broadcast_address(BACNET_ADDRESS *dest) { /* destination address */ int i = 0; /* counter */ if (dest) { dest->mac_len = 1; dest->mac[0] = MSTP_BROADCAST_ADDRESS; dest->net = BACNET_BROADCAST_NETWORK; dest->len = 0; /* always zero when DNET is broadcast */ for (i = 0; i < MAX_MAC_LEN; i++) { dest->adr[i] = 0; } } return; } uint8_t dlmstp_max_info_frames_limit(void) { return MSTP_PDU_PACKET_COUNT; } uint8_t dlmstp_max_master_limit(void) { return 127; }