From 1f591db6e77e7e8197a2304bf2f45ad881eac3d3 Mon Sep 17 00:00:00 2001 From: Steve Karg Date: Fri, 2 Feb 2024 11:18:20 -0600 Subject: [PATCH] Feature/mstp zero config option (#564) * Added a MS/TP zero-config (automatically choose an unused MAC address) using an algorithm that starts with MAC=64 and waits for a random number of PFM (minimum of 8 plus modulo 64) before attempting to choose a MAC sequentially from 64..127. The confirmation uses a 128-bit UUID with the MSTP Test Request frame. The modifications are in src/bacnet/datalink/mstp.c and src/bacnet/datalink/dlmstp.c modules enabling any device to use zero-config if enabled. A working demonstration is in the ports/stm32f4xx for the NUCLEO board. Complete unit testing is included. Options include lurking forever (wait for a router or another master node before joining) or lurking for a minimum time (enables self forming automatic MAC addressing device nodes). --- Makefile | 4 + apps/mstpcap/main.c | 37 +- apps/router/mstpmodule.c | 4 +- ports/linux/dlmstp.c | 14 +- ports/linux/dlmstp_linux.c | 14 +- ports/linux/dlmstp_linux.h | 10 - ports/linux/mstpsnap.c | 14 +- ports/linux/rs485.c | 8 +- ports/linux/rs485.h | 6 +- ports/stm32f4xx/.vscode/launch.json | 4 + ports/stm32f4xx/device.c | 7 +- ports/stm32f4xx/main.c | 29 +- ports/win32/dlmstp-mm.c | 16 +- ports/win32/dlmstp.c | 12 +- ports/win32/rs485.c | 4 +- ports/win32/rs485.h | 4 +- src/bacnet/basic/object/netport.c | 2 + src/bacnet/datalink/dlmstp.c | 203 ++-- src/bacnet/datalink/mstp.c | 743 ++++++++++----- src/bacnet/datalink/mstp.h | 365 ++++---- src/bacnet/datalink/mstpdef.h | 96 +- src/bacnet/datalink/mstptext.c | 15 + src/bacnet/datalink/mstptext.h | 3 + test/CMakeLists.txt | 1 + test/bacnet/datalink/mstp/CMakeLists.txt | 54 ++ test/bacnet/datalink/mstp/src/main.c | 1068 ++++++++++++++++++++++ 26 files changed, 2138 insertions(+), 599 deletions(-) create mode 100644 test/bacnet/datalink/mstp/CMakeLists.txt create mode 100644 test/bacnet/datalink/mstp/src/main.c diff --git a/Makefile b/Makefile index debf25a1..2e320530 100644 --- a/Makefile +++ b/Makefile @@ -396,3 +396,7 @@ clean: ports-clean test: $(MAKE) -s -C test clean $(MAKE) -s -j -C test all + +.PHONY: retest +retest: + $(MAKE) -s -j -C test retest diff --git a/apps/mstpcap/main.c b/apps/mstpcap/main.c index 548fbbe7..cce73f7c 100644 --- a/apps/mstpcap/main.c +++ b/apps/mstpcap/main.c @@ -68,7 +68,7 @@ #define MSTP_HEADER_MAX (2 + 1 + 1 + 1 + 2 + 1) /* local port data - shared with RS-485 */ -static volatile struct mstp_port_struct_t MSTP_Port; +static struct mstp_port_struct_t MSTP_Port; /* track the receive state to know when there is a broken packet */ static MSTP_RECEIVE_STATE MSTP_Receive_State = MSTP_RECEIVE_STATE_IDLE; /* buffers needed by mstp port struct */ @@ -185,7 +185,7 @@ static void mstp_monitor_i_am(uint8_t mac, uint8_t *pdu, uint16_t pdu_len) } static void packet_statistics( - struct timeval *tv, volatile struct mstp_port_struct_t *mstp_port) + struct timeval *tv, struct mstp_port_struct_t *mstp_port) { static struct timeval old_tv = { 0 }; static uint8_t old_frame = 255; @@ -286,8 +286,7 @@ static void packet_statistics( MSTP_Statistics[src].der_reply = delta; } } - if ((mstp_port->ReceivedValidFrame) || - (mstp_port->ReceivedValidFrameNotForUs)) { + if (mstp_port->ReceivedValidFrame) { if ((mstp_port->DataLength <= mstp_port->InputBufferSize) && (mstp_port->DataLength > 0)) { mstp_monitor_i_am( @@ -413,7 +412,7 @@ static void Timer_Silence_Reset(void *pArg) } /* functions used by the MS/TP state machine to put or get data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { (void)mstp_port; @@ -423,7 +422,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ (void)mstp_port; (void)timeout; @@ -437,7 +436,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -447,7 +446,7 @@ void MSTP_Send_Frame( } uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ (void)mstp_port; (void)timeout; @@ -623,7 +622,7 @@ static void write_global_header(void) } static void write_received_packet( - volatile struct mstp_port_struct_t *mstp_port, size_t header_len) + struct mstp_port_struct_t *mstp_port, size_t header_len) { uint32_t ts_sec = 0; /* timestamp seconds */ uint32_t ts_usec = 0; /* timestamp microseconds */ @@ -637,8 +636,7 @@ static void write_received_packet( gettimeofday(&tv, NULL); ts_sec = tv.tv_sec; ts_usec = tv.tv_usec; - if ((mstp_port->ReceivedValidFrame) || - (mstp_port->ReceivedValidFrameNotForUs)) { + if (mstp_port->ReceivedValidFrame) { packet_statistics(&tv, mstp_port); } (void)data_write(&ts_sec, sizeof(ts_sec), 1); @@ -765,7 +763,7 @@ static bool test_global_header(const char *filename) return true; } -static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port) +static bool read_received_packet(struct mstp_port_struct_t *mstp_port) { uint32_t ts_sec = 0; /* timestamp seconds */ uint32_t ts_usec = 0; /* timestamp microseconds */ @@ -824,7 +822,6 @@ static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port) mstp_port->ReceivedInvalidFrame = false; if (mstp_port->DataLength == 0) { mstp_port->ReceivedValidFrame = true; - mstp_port->ReceivedValidFrameNotForUs = true; } } else { mstp_port->ReceivedValidFrame = false; @@ -864,19 +861,16 @@ static bool read_received_packet(volatile struct mstp_port_struct_t *mstp_port) if (mstp_port->DataCRC == 0xF0B8) { mstp_port->ReceivedInvalidFrame = false; mstp_port->ReceivedValidFrame = true; - mstp_port->ReceivedValidFrameNotForUs = true; } else { mstp_port->ReceivedInvalidFrame = true; mstp_port->ReceivedValidFrame = false; - mstp_port->ReceivedValidFrameNotForUs = false; } } else { mstp_port->DataLength = 0; } if (mstp_port->ReceivedInvalidFrame) { Invalid_Frame_Count++; - } else if ((mstp_port->ReceivedValidFrame) || - (mstp_port->ReceivedValidFrameNotForUs)) { + } else if (mstp_port->ReceivedValidFrame) { packet_statistics(&tv, mstp_port); } } else { @@ -984,7 +978,7 @@ static void print_help(char *filename) } /* initialize some of the variables in the MS/TP Receive structure */ -static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port) +static void mstp_structure_init(struct mstp_port_struct_t *mstp_port) { if (mstp_port) { mstp_port->FrameType = FRAME_TYPE_PROPRIETARY_MAX; @@ -996,7 +990,6 @@ static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port) mstp_port->EventCount = 0; mstp_port->ReceivedInvalidFrame = false; mstp_port->ReceivedValidFrame = false; - mstp_port->ReceivedValidFrameNotForUs = false; mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } } @@ -1004,7 +997,7 @@ static void mstp_structure_init(volatile struct mstp_port_struct_t *mstp_port) /* simple test to packetize the data and print it */ int main(int argc, char *argv[]) { - volatile struct mstp_port_struct_t *mstp_port; + struct mstp_port_struct_t *mstp_port; long my_baud = 38400; uint32_t packet_count = 0; uint32_t header_len = 0; @@ -1177,10 +1170,6 @@ int main(int argc, char *argv[]) write_received_packet(mstp_port, MSTP_HEADER_MAX); mstp_structure_init(mstp_port); packet_count++; - } else if (mstp_port->ReceivedValidFrameNotForUs) { - write_received_packet(mstp_port, MSTP_HEADER_MAX); - mstp_structure_init(mstp_port); - packet_count++; } else if (mstp_port->ReceivedInvalidFrame) { if (MSTP_Receive_State == MSTP_RECEIVE_STATE_HEADER) { mstp_port->Index = 0; diff --git a/apps/router/mstpmodule.c b/apps/router/mstpmodule.c index 0390cf32..1762d160 100644 --- a/apps/router/mstpmodule.c +++ b/apps/router/mstpmodule.c @@ -50,9 +50,7 @@ void *dl_mstp_thread(void *pArgs) uint16_t pdu_len; uint8_t shutdown = 0; - shared_port_data.Treply_timeout = 260; shared_port_data.MSTP_Packets = 0; - shared_port_data.Tusage_timeout = 30; shared_port_data.RS485_Handle = -1; shared_port_data.RS485_Baud = B38400; shared_port_data.RS485MOD = 0; @@ -95,6 +93,8 @@ void *dl_mstp_thread(void *pArgs) if (!dlmstp_init(&mstp_port, port->iface)) { printf("MSTP %s init failed. Stop.\n", port->iface); } + mstp_port.Treply_timeout = 260; + mstp_port.Tusage_timeout = 30; port->port_id = create_msgbox(); if (port->port_id == INVALID_MSGBOX_ID) { diff --git a/ports/linux/dlmstp.c b/ports/linux/dlmstp.c index 28be145d..0fc0f26b 100644 --- a/ports/linux/dlmstp.c +++ b/ports/linux/dlmstp.c @@ -75,7 +75,7 @@ static bool run_thread; /*RT_TASK Receive_Task, Fsm_Task;*/ /* local MS/TP port data - shared with RS-485 */ -static volatile struct mstp_port_struct_t MSTP_Port; +static struct mstp_port_struct_t MSTP_Port; /* buffers needed by mstp port struct */ static uint8_t TxBuffer[DLMSTP_MPDU_MAX]; static uint8_t RxBuffer[DLMSTP_MPDU_MAX]; @@ -287,7 +287,7 @@ static void *dlmstp_master_fsm_task(void *pArg) if (MSTP_Port.ReceivedValidFrame || MSTP_Port.ReceivedInvalidFrame) { run_master = true; } else { - silence = MSTP_Port.SilenceTimer(NULL); + silence = MSTP_Port.SilenceTimer(&MSTP_Port); switch (MSTP_Port.master_state) { case MSTP_MASTER_STATE_IDLE: if (silence >= Tno_token) @@ -353,7 +353,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) } /* for the MS/TP state machine to use for putting received data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { uint16_t pdu_len = 0; @@ -384,8 +384,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ -uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) +uint16_t MSTP_Get_Send(struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; uint8_t frame_type = 0; @@ -421,7 +420,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -584,8 +583,7 @@ static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, } /* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ -uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) +uint16_t MSTP_Get_Reply(struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; /* return value */ bool matched = false; diff --git a/ports/linux/dlmstp_linux.c b/ports/linux/dlmstp_linux.c index 7a1dc266..3d9b3771 100644 --- a/ports/linux/dlmstp_linux.c +++ b/ports/linux/dlmstp_linux.c @@ -228,7 +228,7 @@ void *dlmstp_receive_fsm_task(void *pArg) do { RS485_Check_UART_Data(mstp_port); MSTP_Receive_Frame_FSM( - (volatile struct mstp_port_struct_t *)pArg); + (struct mstp_port_struct_t *)pArg); received_frame = mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame; if (received_frame) { @@ -274,11 +274,11 @@ void *dlmstp_master_fsm_task(void *pArg) run_master = true; break; case MSTP_MASTER_STATE_WAIT_FOR_REPLY: - if (silence >= poSharedData->Treply_timeout) + if (silence >= mstp_port->Treply_timeout) run_master = true; break; case MSTP_MASTER_STATE_POLL_FOR_MASTER: - if (silence >= poSharedData->Tusage_timeout) + if (silence >= mstp_port->Tusage_timeout) run_master = true; break; default: @@ -324,7 +324,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) } /* for the MS/TP state machine to use for putting received data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { uint16_t pdu_len = 0; SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData; @@ -353,7 +353,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; uint8_t frame_type = 0; @@ -391,7 +391,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -555,7 +555,7 @@ bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, /* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; /* return value */ bool matched = false; diff --git a/ports/linux/dlmstp_linux.h b/ports/linux/dlmstp_linux.h index 1af53830..de3538d9 100644 --- a/ports/linux/dlmstp_linux.h +++ b/ports/linux/dlmstp_linux.h @@ -87,16 +87,6 @@ typedef struct shared_mstp_data { /* buffers needed by mstp port struct */ uint8_t TxBuffer[DLMSTP_MPDU_MAX]; uint8_t RxBuffer[DLMSTP_MPDU_MAX]; - /* 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.) */ - uint16_t Treply_timeout; - /* The minimum 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 100 milliseconds.) */ - uint8_t Tusage_timeout; /* Timer that indicates line silence - and functions */ uint16_t SilenceTime; diff --git a/ports/linux/mstpsnap.c b/ports/linux/mstpsnap.c index 72df7c3b..4fda8d4c 100644 --- a/ports/linux/mstpsnap.c +++ b/ports/linux/mstpsnap.c @@ -59,7 +59,7 @@ #endif /* local port data - shared with RS-485 */ -static volatile struct mstp_port_struct_t MSTP_Port; +static struct mstp_port_struct_t MSTP_Port; /* buffers needed by mstp port struct */ static uint8_t RxBuffer[DLMSTP_MPDU_MAX]; static uint8_t TxBuffer[DLMSTP_MPDU_MAX]; @@ -83,7 +83,7 @@ static void Timer_Silence_Reset(void *pArg) } /* functions used by the MS/TP state machine to put or get data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { (void)mstp_port; @@ -93,7 +93,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ (void)mstp_port; (void)timeout; @@ -107,7 +107,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -117,7 +117,7 @@ void MSTP_Send_Frame( } uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ (void)mstp_port; (void)timeout; @@ -165,7 +165,7 @@ static int network_init(const char *name, int protocol) } static void snap_received_packet( - volatile struct mstp_port_struct_t *mstp_port, int sockfd) + struct mstp_port_struct_t *mstp_port, int sockfd) { uint16_t mtu_len = 0; /* number of octets of packet saved in file */ unsigned i = 0; /* counter */ @@ -241,7 +241,7 @@ void signal_init(void) /* simple test to packetize the data and print it */ int main(int argc, char *argv[]) { - volatile struct mstp_port_struct_t *mstp_port; + struct mstp_port_struct_t *mstp_port; long my_baud = 38400; uint32_t packet_count = 0; int sockfd = -1; diff --git a/ports/linux/rs485.c b/ports/linux/rs485.c index 8620ed0e..fed0b37d 100644 --- a/ports/linux/rs485.c +++ b/ports/linux/rs485.c @@ -217,7 +217,7 @@ uint32_t RS485_Get_Baud_Rate(void) * ALGORITHM: none * NOTES: none *****************************************************************************/ -uint32_t RS485_Get_Port_Baud_Rate(volatile struct mstp_port_struct_t *mstp_port) +uint32_t RS485_Get_Port_Baud_Rate(struct mstp_port_struct_t *mstp_port) { uint32_t baud = 0; SHARED_MSTP_DATA *poSharedData = (SHARED_MSTP_DATA *)mstp_port->UserData; @@ -382,7 +382,7 @@ bool RS485_Set_Baud_Rate(uint32_t baud) * NOTES: none *****************************************************************************/ void RS485_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + struct mstp_port_struct_t *mstp_port, /* port specific data */ uint8_t *buffer, /* frame to send (up to 501 bytes of data) */ uint16_t nbytes) { /* number of bytes of data (up to 501) */ @@ -458,7 +458,7 @@ void RS485_Send_Frame( * ALGORITHM: none * NOTES: none *****************************************************************************/ -void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port) { fd_set input; struct timeval waiter; @@ -694,7 +694,7 @@ void RS485_Print_Ports(void) #include int main(int argc, char *argv[]) { - volatile struct mstp_port_struct_t mstp_port = { 0 }; + struct mstp_port_struct_t mstp_port = { 0 }; uint8_t token_buf[8] = { 0x55, 0xFF, 0x00, 0x7E, 0x07, 0x00, 0x00, 0xFD }; uint8_t pfm_buf[8] = { 0x55, 0xFF, 0x01, 0x67, 0x07, 0x00, 0x00, 0x3E }; long baud = 38400; diff --git a/ports/linux/rs485.h b/ports/linux/rs485.h index 143e6239..b33f10df 100644 --- a/ports/linux/rs485.h +++ b/ports/linux/rs485.h @@ -57,16 +57,16 @@ extern "C" { BACNET_STACK_EXPORT void RS485_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + struct mstp_port_struct_t *mstp_port, /* port specific data */ uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ uint16_t nbytes); /* number of bytes of data (up to 501) */ BACNET_STACK_EXPORT void RS485_Check_UART_Data( - volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + struct mstp_port_struct_t *mstp_port); /* port specific data */ BACNET_STACK_EXPORT uint32_t RS485_Get_Port_Baud_Rate( - volatile struct mstp_port_struct_t *mstp_port); + struct mstp_port_struct_t *mstp_port); BACNET_STACK_EXPORT uint32_t RS485_Get_Baud_Rate( void); diff --git a/ports/stm32f4xx/.vscode/launch.json b/ports/stm32f4xx/.vscode/launch.json index d329ff37..69a2681f 100644 --- a/ports/stm32f4xx/.vscode/launch.json +++ b/ports/stm32f4xx/.vscode/launch.json @@ -17,6 +17,10 @@ "searchDir": [], "svdFile":"${workspaceRoot}/stm32f429.svd", "runToEntryPoint": "main", + "liveWatch": { + "enabled": true, + "samplesPerSecond": 1 + }, "swoConfig": { "enabled": true, "cpuFrequency": 8000000, diff --git a/ports/stm32f4xx/device.c b/ports/stm32f4xx/device.c index c3626ab1..1c70b706 100644 --- a/ports/stm32f4xx/device.c +++ b/ports/stm32f4xx/device.c @@ -77,7 +77,7 @@ static struct my_object_functions { properties that are writable or that may change. The properties that are constant can be hard coded into the read-property encoding. */ -static uint32_t Object_Instance_Number = 103; +static uint32_t Object_Instance_Number = BACNET_MAX_INSTANCE; static BACNET_DEVICE_STATUS System_Status = STATUS_OPERATIONAL; static BACNET_CHARACTER_STRING My_Object_Name; static const char *Device_Name_Default = "stm32f4xx"; @@ -1131,9 +1131,8 @@ void Device_Init(object_functions_t *object_table) pObject++; } dcc_set_status_duration(COMMUNICATION_ENABLE, 0); - if (Object_Instance_Number >= BACNET_MAX_INSTANCE) { - Object_Instance_Number = 103; - srand(Object_Instance_Number); + if (Object_Instance_Number > BACNET_MAX_INSTANCE) { + Object_Instance_Number = BACNET_MAX_INSTANCE; } characterstring_init_ansi(&My_Object_Name, Device_Name_Default); } diff --git a/ports/stm32f4xx/main.c b/ports/stm32f4xx/main.c index 275c45b9..891c0afb 100644 --- a/ports/stm32f4xx/main.c +++ b/ports/stm32f4xx/main.c @@ -22,7 +22,7 @@ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * *************************************************************************/ - +#include #include #include #include @@ -30,7 +30,9 @@ #include "stm32f4xx.h" #include "stm32f4xx_pwr.h" #include "stm32f4xx_rcc.h" +#include "stm32f4xx_rng.h" #include "system_stm32f4xx.h" +#include "bacnet/basic/object/device.h" #include "bacnet/basic/sys/mstimer.h" #include "bacnet/basic/sys/ringbuf.h" #include "bacnet/datalink/datalink.h" @@ -96,13 +98,36 @@ int main(void) MSTP_Port.OutputBuffer = Output_Buffer; MSTP_Port.OutputBufferSize = sizeof(Output_Buffer); /* user data */ + MSTP_Port.ZeroConfigEnabled = true; + MSTP_Port.SlaveNodeEnabled = false; MSTP_User_Data.RS485_Driver = &RS485_Driver; MSTP_Port.UserData = &MSTP_User_Data; dlmstp_init((char *)&MSTP_Port); - dlmstp_set_mac_address(2); + if (MSTP_Port.ZeroConfigEnabled) { + dlmstp_set_mac_address(255); + } else { + /* FIXME: get the address from hardware DIP or from EEPROM */ + dlmstp_set_mac_address(1); + } + /* FIXME: get the baud rate from hardware DIP or from EEPROM */ dlmstp_set_baud_rate(DLMSTP_BAUD_RATE_DEFAULT); /* initialize application layer*/ bacnet_init(); + /* FIXME: get the device ID from EEPROM */ + Device_Set_Object_Instance_Number(103); + /* seed stdlib rand() with device-id to get pweudo consisten + zero-config poll slot, or use hardware RNG to get a more random slot */ +#ifdef BACNET_ZERO_CONFIG_RNG_HARDWARE + /* enable the random number generator hardware */ + RCC_AHB2PeriphClockCmd(RCC_AHB2Periph_RNG, ENABLE); + RNG_Cmd(ENABLE); + while (RNG_GetFlagStatus(RNG_FLAG_DRDY) == RESET) { + /* wait for 32-bit random number to generate */ + } + srand(RNG_GetRandomNumber()); +#else + srand(Device_Object_Instance_Number()); +#endif for (;;) { if (mstimer_expired(&Blink_Timer)) { mstimer_reset(&Blink_Timer); diff --git a/ports/win32/dlmstp-mm.c b/ports/win32/dlmstp-mm.c index b76cc4af..f5910181 100644 --- a/ports/win32/dlmstp-mm.c +++ b/ports/win32/dlmstp-mm.c @@ -52,7 +52,7 @@ static HANDLE Receive_Packet_Flag; HANDLE Received_Frame_Flag; static DLMSTP_PACKET Transmit_Packet; /* local MS/TP port data - shared with RS-485 */ -volatile struct mstp_port_struct_t MSTP_Port; +static struct mstp_port_struct_t MSTP_Port; /* buffers needed by mstp port struct */ static uint8_t TxBuffer[DLMSTP_MPDU_MAX]; static uint8_t RxBuffer[DLMSTP_MPDU_MAX]; @@ -62,11 +62,12 @@ static uint32_t TimeBeginPeriod; /* 1-millisecond target resolution */ #define TARGET_RESOLUTION 1 -static uint16_t Timer_Silence(void) +static uint16_t Timer_Silence(void *arg) { uint32_t now = timeGetTime(); uint32_t delta_time = 0; + (void)arg; if (SilenceStartTime < now) { delta_time = now - SilenceStartTime; } else { @@ -79,8 +80,9 @@ static uint16_t Timer_Silence(void) return (uint16_t)delta_time; } -static void Timer_Silence_Reset(void) +static void Timer_Silence_Reset(void *arg) { + (void)arg; SilenceStartTime = timeGetTime(); } @@ -241,7 +243,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) } /* for the MS/TP state machine to use for putting received data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { uint16_t pdu_len = 0; BOOL rc; @@ -266,7 +268,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; uint8_t destination = 0; /* destination address */ @@ -303,7 +305,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -420,7 +422,7 @@ bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, /* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; /* return value */ uint8_t destination = 0; /* destination address */ diff --git a/ports/win32/dlmstp.c b/ports/win32/dlmstp.c index c8d489f2..45c8bb24 100644 --- a/ports/win32/dlmstp.c +++ b/ports/win32/dlmstp.c @@ -51,7 +51,7 @@ static HANDLE Receive_Packet_Flag; HANDLE Received_Frame_Flag; static DLMSTP_PACKET Transmit_Packet; /* local MS/TP port data - shared with RS-485 */ -volatile struct mstp_port_struct_t MSTP_Port; +struct mstp_port_struct_t MSTP_Port; /* buffers needed by mstp port struct */ static uint8_t TxBuffer[DLMSTP_MPDU_MAX]; static uint8_t RxBuffer[DLMSTP_MPDU_MAX]; @@ -71,11 +71,13 @@ static struct mstimer Silence_Timer; /* Timer that indicates line silence - and functions */ static uint32_t Timer_Silence(void *pArg) { + (void)pArg; return mstimer_elapsed(&Silence_Timer); } static void Timer_Silence_Reset(void *pArg) { + (void)pArg; mstimer_set(&Silence_Timer, 0); } @@ -226,7 +228,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) } /* for the MS/TP state machine to use for putting received data */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { uint16_t pdu_len = 0; BOOL rc; @@ -252,7 +254,7 @@ uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; uint8_t destination = 0; /* destination address */ @@ -288,7 +290,7 @@ uint16_t MSTP_Get_Send( * @param nbytes - number of bytes of data to send */ void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, + struct mstp_port_struct_t *mstp_port, uint8_t * buffer, uint16_t nbytes) { @@ -405,7 +407,7 @@ static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, /* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ uint16_t pdu_len = 0; /* return value */ uint8_t destination = 0; /* destination address */ diff --git a/ports/win32/rs485.c b/ports/win32/rs485.c index abb51ba0..f02bccf4 100644 --- a/ports/win32/rs485.c +++ b/ports/win32/rs485.c @@ -421,7 +421,7 @@ bool RS485_Set_Baud_Rate(uint32_t baud) /* Transmits a Frame on the wire */ void RS485_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + struct mstp_port_struct_t *mstp_port, /* port specific data */ uint8_t *buffer, /* frame to send (up to 501 bytes of data) */ uint16_t nbytes) { /* number of bytes of data (up to 501) */ @@ -453,7 +453,7 @@ void RS485_Send_Frame( } /* called by timer, interrupt(?) or other thread */ -void RS485_Check_UART_Data(volatile struct mstp_port_struct_t *mstp_port) +void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port) { char lpBuf[1]; DWORD dwRead = 0; diff --git a/ports/win32/rs485.h b/ports/win32/rs485.h index b064a971..7a89569c 100644 --- a/ports/win32/rs485.h +++ b/ports/win32/rs485.h @@ -58,13 +58,13 @@ extern "C" { BACNET_STACK_EXPORT void RS485_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port specific data */ + struct mstp_port_struct_t *mstp_port, /* port specific data */ uint8_t * buffer, /* frame to send (up to 501 bytes of data) */ uint16_t nbytes); /* number of bytes of data (up to 501) */ BACNET_STACK_EXPORT void RS485_Check_UART_Data( - volatile struct mstp_port_struct_t *mstp_port); /* port specific data */ + struct mstp_port_struct_t *mstp_port); /* port specific data */ BACNET_STACK_EXPORT uint32_t RS485_Get_Baud_Rate( diff --git a/src/bacnet/basic/object/netport.c b/src/bacnet/basic/object/netport.c index 7d0bd7a3..f1ec0994 100644 --- a/src/bacnet/basic/object/netport.c +++ b/src/bacnet/basic/object/netport.c @@ -2631,6 +2631,7 @@ bool Network_Port_Read_Range( (void)pInfo; pRequest->error_class = ERROR_CLASS_PROPERTY; pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + (void)pInfo; #endif break; case PROP_BBMD_FOREIGN_DEVICE_TABLE: @@ -2642,6 +2643,7 @@ bool Network_Port_Read_Range( (void)pInfo; pRequest->error_class = ERROR_CLASS_PROPERTY; pRequest->error_code = ERROR_CODE_UNKNOWN_PROPERTY; + (void)pInfo; #endif break; default: diff --git a/src/bacnet/datalink/dlmstp.c b/src/bacnet/datalink/dlmstp.c index dbbd9037..5633b791 100644 --- a/src/bacnet/datalink/dlmstp.c +++ b/src/bacnet/datalink/dlmstp.c @@ -41,7 +41,7 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest, { int bytes_sent = 0; unsigned i = 0; /* loop counter */ - struct dlmstp_user_data_t *port = NULL; + struct dlmstp_user_data_t *user = NULL; struct dlmstp_packet *pkt; if (!MSTP_Port) { @@ -50,8 +50,8 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest, if (!MSTP_Port->UserData) { return 0; } - port = MSTP_Port->UserData; - pkt = (struct dlmstp_packet *)(void *)Ringbuf_Data_Peek(&port->PDU_Queue); + user = MSTP_Port->UserData; + pkt = (struct dlmstp_packet *)(void *)Ringbuf_Data_Peek(&user->PDU_Queue); if (pkt && (pdu_len <= DLMSTP_MPDU_MAX)) { if (npdu_data->data_expecting_reply) { pkt->frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; @@ -71,7 +71,7 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest, pkt->address.mac[0] = MSTP_BROADCAST_ADDRESS; pkt->address.len = 0; } - if (Ringbuf_Data_Put(&port->PDU_Queue, (uint8_t *)pkt)) { + if (Ringbuf_Data_Put(&user->PDU_Queue, (uint8_t *)pkt)) { bytes_sent = pdu_len; } } @@ -86,30 +86,30 @@ int dlmstp_send_pdu(BACNET_ADDRESS *dest, * @return amount of PDU data */ uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { uint16_t pdu_len = 0; struct dlmstp_packet *pkt; - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; if (!mstp_port) { return 0; } - port = (struct dlmstp_user_data_t *)mstp_port->UserData; - if (!port) { + user = (struct dlmstp_user_data_t *)mstp_port->UserData; + if (!user) { return 0; } - if (Ringbuf_Empty(&port->PDU_Queue)) { + if (Ringbuf_Empty(&user->PDU_Queue)) { return 0; } /* look at next PDU in queue without removing it */ - pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&port->PDU_Queue); + pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&user->PDU_Queue); /* convert the PDU into the MSTP Frame */ pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], mstp_port->OutputBufferSize, pkt->frame_type, pkt->address.mac[0], mstp_port->This_Station, &pkt->pdu[0], pkt->pdu_len); - port->Statistics.transmit_pdu_counter++; - (void)Ringbuf_Pop(&port->PDU_Queue, NULL); + user->Statistics.transmit_pdu_counter++; + (void)Ringbuf_Pop(&user->PDU_Queue, NULL); return pdu_len; } @@ -148,15 +148,12 @@ static bool MSTP_Compare_Data_Expecting_Reply( request_pdu = &mstp_port->InputBuffer[0]; request_pdu_len = mstp_port->DataLength; src_address = mstp_port->SourceAddress; - - /* unused parameters */ - request_pdu_len = request_pdu_len; - reply_pdu_len = reply_pdu_len; /* decode the request data */ request.address.mac[0] = src_address; request.address.mac_len = 1; - offset = (uint16_t)npdu_decode( - &request_pdu[0], NULL, &request.address, &request.npdu_data); + offset = bacnet_npdu_decode( + &request_pdu[0], request_pdu_len, NULL, &request.address, + &request.npdu_data); if (request.npdu_data.network_layer_message) { return false; } @@ -172,8 +169,9 @@ static bool MSTP_Compare_Data_Expecting_Reply( request.service_choice = request_pdu[offset + 3]; /* decode the reply data */ bacnet_address_copy(&reply.address, dest_address); - offset = (uint16_t)npdu_decode( - &reply_pdu[0], &reply.address, NULL, &reply.npdu_data); + offset = bacnet_npdu_decode( + &reply_pdu[0], reply_pdu_len, &reply.address, NULL, + &reply.npdu_data); if (reply.npdu_data.network_layer_message) { return false; } @@ -244,26 +242,26 @@ static bool MSTP_Compare_Data_Expecting_Reply( * @return number of bytes, or 0 if no reply is available */ uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) + struct mstp_port_struct_t *mstp_port, unsigned timeout) { uint16_t pdu_len = 0; bool matched = false; struct dlmstp_packet packet = { 0 }; - struct dlmstp_user_data_t *port = NULL; + struct dlmstp_user_data_t *user = NULL; struct dlmstp_packet *pkt; if (!mstp_port) { return 0; } - port = mstp_port->UserData; - if (!port) { + user = mstp_port->UserData; + if (!user) { return 0; } - if (Ringbuf_Empty(&port->PDU_Queue)) { + if (Ringbuf_Empty(&user->PDU_Queue)) { return 0; } /* look at next PDU in queue without removing it */ - pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&port->PDU_Queue); + pkt = (struct dlmstp_packet *)(void *)Ringbuf_Peek(&user->PDU_Queue); /* is this the reply to the DER? */ matched = MSTP_Compare_Data_Expecting_Reply( mstp_port, pkt->pdu, pkt->pdu_len, &pkt->address); @@ -274,8 +272,8 @@ uint16_t MSTP_Get_Reply( pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], mstp_port->OutputBufferSize, pkt->frame_type, packet.address.mac[0], mstp_port->This_Station, &pkt->pdu[0], pkt->pdu_len); - port->Statistics.transmit_pdu_counter++; - (void)Ringbuf_Pop(&port->PDU_Queue, NULL); + user->Statistics.transmit_pdu_counter++; + (void)Ringbuf_Pop(&user->PDU_Queue, NULL); return pdu_len; } @@ -286,70 +284,48 @@ uint16_t MSTP_Get_Reply( * @param buffer - buffer to send * @param nbytes - number of bytes of data to send */ -void MSTP_Send_Frame(volatile struct mstp_port_struct_t *mstp_port, +void MSTP_Send_Frame(struct mstp_port_struct_t *mstp_port, uint8_t *buffer, uint16_t nbytes) { - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; struct dlmstp_rs485_driver *driver; if (!mstp_port) { return; } - port = mstp_port->UserData; - if (!port) { + user = mstp_port->UserData; + if (!user) { return; } - driver = port->RS485_Driver; + driver = user->RS485_Driver; if (!driver) { return; } driver->send(buffer, nbytes); - port->Statistics.transmit_frame_counter++; + user->Statistics.transmit_frame_counter++; } /** * @brief MS/TP state machine received a frame * @return number of bytes queued, or 0 if unable to be queued */ -uint16_t MSTP_Put_Receive(volatile struct mstp_port_struct_t *mstp_port) +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) { - struct dlmstp_user_data_t *port = NULL; + struct dlmstp_user_data_t *user = NULL; if (!mstp_port) { return 0; } - port = mstp_port->UserData; - if (!port) { + user = mstp_port->UserData; + if (!user) { return 0; } - port->ReceivePacketPending = true; + user->ReceivePacketPending = true; return mstp_port->DataLength; } -/** - * @brief Baud rate determines turnaround time. - * The minimum time after the end of the stop bit of the final octet of a - * received frame before a node may enable its EIA-485 driver: 40 bit times. - * At 9600 baud, 40 bit times would be about 4.166 milliseconds - * At 19200 baud, 40 bit times would be about 2.083 milliseconds - * At 38400 baud, 40 bit times would be about 1.041 milliseconds - * At 57600 baud, 40 bit times would be about 0.694 milliseconds - * At 76800 baud, 40 bit times would be about 0.520 milliseconds - * At 115200 baud, 40 bit times would be about 0.347 milliseconds - * 40 bits is 4 octets including a start and stop bit with each octet - * @param baud_rate - baud rate in bits per second - * @return: amount of whole milliseconds to wait 1..5 - */ -static uint32_t dlmstp_receive_turnaround_time(uint32_t baud_rate) -{ - if (baud_rate == 0) { - baud_rate = 9600; - } - return (1 + ((Tturnaround * 1000) / baud_rate)); -} - /** * @brief Run the MS/TP state machines, and get packet if available * @param pdu - place to put PDU data for the caller @@ -363,11 +339,10 @@ uint16_t dlmstp_receive( { uint16_t pdu_len = 0; uint8_t data_register = 0; - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; struct dlmstp_rs485_driver *driver; uint16_t i; uint32_t milliseconds; - uint32_t turnaround_milliseconds; if (!MSTP_Port) { return 0; @@ -375,11 +350,11 @@ uint16_t dlmstp_receive( if (!MSTP_Port->UserData) { return 0; } - port = MSTP_Port->UserData; - if (!port) { + user = MSTP_Port->UserData; + if (!user) { return 0; } - driver = port->RS485_Driver; + driver = user->RS485_Driver; if (!driver) { return 0; } @@ -392,7 +367,6 @@ uint16_t dlmstp_receive( } /* only do receive state machine while we don't have a frame */ while ((MSTP_Port->ReceivedValidFrame == false) && - (MSTP_Port->ReceivedValidFrameNotForUs == false) && (MSTP_Port->ReceivedInvalidFrame == false)) { MSTP_Port->DataAvailable = driver->read(&data_register); if (MSTP_Port->DataAvailable) { @@ -404,39 +378,35 @@ uint16_t dlmstp_receive( break; } } - if (MSTP_Port->ReceivedValidFrameNotForUs || - MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame) { + if (MSTP_Port->ReceivedValidFrame || MSTP_Port->ReceivedInvalidFrame) { /* delay after reception before transmitting - per MS/TP spec */ - turnaround_milliseconds = - dlmstp_receive_turnaround_time(driver->baud_rate()); milliseconds = MSTP_Port->SilenceTimer(MSTP_Port); - if (milliseconds < turnaround_milliseconds) { + if (milliseconds < MSTP_Port->Tturnaround_timeout) { /* we're waiting; do nothing else */ return 0; } } - if (MSTP_Port->ReceivedValidFrameNotForUs) { - MSTP_Port->ReceivedValidFrameNotForUs = false; - port->Statistics.receive_valid_frame_counter++; - } if (MSTP_Port->ReceivedValidFrame) { - port->Statistics.receive_valid_frame_counter++; + user->Statistics.receive_valid_frame_counter++; } if (MSTP_Port->ReceivedInvalidFrame) { - port->Statistics.receive_invalid_frame_counter++; + user->Statistics.receive_invalid_frame_counter++; } if (MSTP_Port->receive_state == MSTP_RECEIVE_STATE_IDLE) { - /* only do master state machine while rx is idle */ - if (MSTP_Port->This_Station <= DEFAULT_MAX_MASTER) { + /* only node state machines while rx is idle */ + if (MSTP_Port->SlaveNodeEnabled) { + MSTP_Slave_Node_FSM(MSTP_Port); + } else if ((MSTP_Port->This_Station <= DEFAULT_MAX_MASTER) || + MSTP_Port->ZeroConfigEnabled) { while (MSTP_Master_Node_FSM(MSTP_Port)) { /* do nothing while some states fast transition */ }; } } /* see if there is a packet available */ - if (port->ReceivePacketPending) { - port->ReceivePacketPending = false; - port->Statistics.receive_pdu_counter++; + if (user->ReceivePacketPending) { + user->ReceivePacketPending = false; + user->Statistics.receive_pdu_counter++; pdu_len = MSTP_Port->DataLength; if (pdu_len > max_pdu) { /* PDU is too large */ @@ -498,11 +468,8 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS *src, uint8_t mstp_address) */ void dlmstp_set_mac_address(uint8_t mac_address) { - /* Master Nodes can only have address 0-127 */ - if (mac_address <= 127) { - if (MSTP_Port) { - MSTP_Port->This_Station = mac_address; - } + if (MSTP_Port) { + MSTP_Port->This_Station = mac_address; } return; @@ -648,12 +615,12 @@ void dlmstp_get_broadcast_address(BACNET_ADDRESS *dest) bool dlmstp_send_pdu_queue_empty(void) { bool status = false; - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; if (MSTP_Port) { - port = MSTP_Port->UserData; - if (port) { - status = Ringbuf_Empty(&port->PDU_Queue); + user = MSTP_Port->UserData; + if (user) { + status = Ringbuf_Empty(&user->PDU_Queue); } } @@ -667,12 +634,12 @@ bool dlmstp_send_pdu_queue_empty(void) bool dlmstp_send_pdu_queue_full(void) { bool status = false; - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; if (MSTP_Port) { - port = MSTP_Port->UserData; - if (port) { - status = Ringbuf_Full(&port->PDU_Queue); + user = MSTP_Port->UserData; + if (user) { + status = Ringbuf_Full(&user->PDU_Queue); } } @@ -686,7 +653,7 @@ bool dlmstp_send_pdu_queue_full(void) */ void dlmstp_set_baud_rate(uint32_t baud) { - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; struct dlmstp_rs485_driver *driver; if (!MSTP_Port) { @@ -695,12 +662,20 @@ void dlmstp_set_baud_rate(uint32_t baud) if (!MSTP_Port->UserData) { return; } - port = MSTP_Port->UserData; - driver = port->RS485_Driver; + user = MSTP_Port->UserData; + driver = user->RS485_Driver; if (!driver) { return; } - driver->baud_rate_set(baud); + if (driver->baud_rate_set(baud)) { + /* Tframe_abort=60 bit times, not to exceed 100 milliseconds.*/ + if (MSTP_Port->Tframe_abort <= 7) { + /* within baud range, so auto-calculate range based on baud */ + MSTP_Port->Tframe_abort = 1+((60*1000UL)/baud); + } + /* Tturnaround=40 bit times */ + MSTP_Port->Tturnaround_timeout = 1 + ((Tturnaround * 1000) / baud); + } } /** @@ -709,7 +684,7 @@ void dlmstp_set_baud_rate(uint32_t baud) */ uint32_t dlmstp_baud_rate(void) { - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; struct dlmstp_rs485_driver *driver; if (!MSTP_Port) { @@ -718,8 +693,8 @@ uint32_t dlmstp_baud_rate(void) if (!MSTP_Port->UserData) { return 0; } - port = MSTP_Port->UserData; - driver = port->RS485_Driver; + user = MSTP_Port->UserData; + driver = user->RS485_Driver; if (!driver) { return 0; } @@ -733,7 +708,7 @@ uint32_t dlmstp_baud_rate(void) */ void dlmstp_fill_statistics(struct dlmstp_statistics *statistics) { - struct dlmstp_user_data_t *port; + struct dlmstp_user_data_t *user; if (!MSTP_Port) { return; @@ -741,12 +716,12 @@ void dlmstp_fill_statistics(struct dlmstp_statistics *statistics) if (!MSTP_Port->UserData) { return; } - port = MSTP_Port->UserData; - if (!port) { + user = MSTP_Port->UserData; + if (!user) { return; } if (statistics) { - *statistics = port->Statistics; + *statistics = user->Statistics; } } @@ -821,18 +796,18 @@ void dlmstp_silence_reset(void *arg) */ bool dlmstp_init(char *ifname) { - struct dlmstp_user_data_t *user_data; + struct dlmstp_user_data_t *user; MSTP_Port = (struct mstp_port_struct_t *)ifname; if (MSTP_Port) { MSTP_Port->SilenceTimer = dlmstp_silence_milliseconds; MSTP_Port->SilenceTimerReset = dlmstp_silence_reset; - user_data = (struct dlmstp_user_data_t *)MSTP_Port->UserData; - if (user_data && !user_data->Initialized) { - Ringbuf_Init(&user_data->PDU_Queue, - (volatile uint8_t *)user_data->PDU_Buffer, - sizeof(user_data->PDU_Buffer), DLMSTP_MAX_INFO_FRAMES); + user = (struct dlmstp_user_data_t *)MSTP_Port->UserData; + if (user && !user->Initialized) { + Ringbuf_Init(&user->PDU_Queue, + (volatile uint8_t *)user->PDU_Buffer, + sizeof(user->PDU_Buffer), DLMSTP_MAX_INFO_FRAMES); MSTP_Init(MSTP_Port); - user_data->Initialized = true; + user->Initialized = true; } } diff --git a/src/bacnet/datalink/mstp.c b/src/bacnet/datalink/mstp.c index b8ea9c6f..a34925bb 100644 --- a/src/bacnet/datalink/mstp.c +++ b/src/bacnet/datalink/mstp.c @@ -48,12 +48,15 @@ #include #include +#include +#include #if PRINT_ENABLED #include #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" @@ -119,50 +122,6 @@ static inline void printf_master(const char *format, ...) /* least significant octet first */ /* (pad): (optional) at most one octet of padding: X'FF' */ -/* 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 95 -#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 - -/* Repeater turnoff delay. The duration of a continuous logical one state */ -/* at the active input port of an MS/TP repeater after which the repeater */ -/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ -#ifndef Troff -#define Troff 30 -#endif - -/* 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 295 -#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 - /* we need to be able to increment without rolling over */ #define INCREMENT_AND_LIMIT_UINT8(x) \ { \ @@ -170,8 +129,12 @@ static inline void printf_master(const char *format, ...) x++; \ } -bool MSTP_Line_Active(volatile struct mstp_port_struct_t *mstp_port) +bool MSTP_Line_Active(struct mstp_port_struct_t *mstp_port) { + if (!mstp_port) { + return false; + } + return (mstp_port->EventCount > Nmin_octets); } @@ -179,6 +142,9 @@ 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; @@ -219,7 +185,7 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address) * @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 (up to 501) + * @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, @@ -237,26 +203,25 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, bool cobs_bacnet_frame = false; /* true for COBS BACnet frames */ /* not enough to do a header */ - if (buffer_size < 8) { - return 0; + if (buffer && (buffer_size >= 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; } - 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 > 501) || ((frame_type >= Nmin_COBS_type) && - (frame_type <= Nmax_COBS_type))) { + 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; @@ -291,8 +256,10 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, index = index + cobs_len - 2; } else if (data_len > 0) { while (data_len && data && (index < buffer_size)) { - buffer[index] = *data; - crc16 = CRC_Calc_Data(buffer[index], crc16); + if (buffer) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + } data++; index++; data_len--; @@ -301,9 +268,13 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, return 0; } crc16 = ~crc16; - buffer[index] = crc16 & 0xFF; /* LSB first */ + if (buffer) { + buffer[index] = crc16 & 0xFF; /* LSB first */ + } index++; - buffer[index] = crc16 >> 8; + if (buffer) { + buffer[index] = crc16 >> 8; + } index++; } @@ -317,10 +288,9 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, * @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 (up to 501) + * @param data_len - number of bytes of data */ -void MSTP_Create_And_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, +void MSTP_Create_And_Send_Frame(struct mstp_port_struct_t *mstp_port, uint8_t frame_type, uint8_t destination, uint8_t source, @@ -329,17 +299,22 @@ void MSTP_Create_And_Send_Frame( { uint16_t len = 0; /* number of bytes to send */ - len = MSTP_Create_Frame((uint8_t *)&mstp_port->OutputBuffer[0], - mstp_port->OutputBufferSize, frame_type, destination, source, data, - data_len); + len = + MSTP_Create_Frame(mstp_port->OutputBuffer, mstp_port->OutputBufferSize, + frame_type, destination, source, data, data_len); MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], len); /* FIXME: be sure to reset SilenceTimer() after each octet is sent! */ } -void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) +/** + * @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", @@ -348,24 +323,23 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->EventCount, mstp_port->DataLength, mstp_port->SilenceTimer((void *)mstp_port)); switch (mstp_port->receive_state) { - /* In the IDLE state, the node waits for the beginning of a frame. - */ 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); - /* wait for the start of a frame. */ } else if (mstp_port->DataAvailable == true) { + /* wait for the start of a frame. */ printf_receive_data("MSTP Rx: %02X ", mstp_port->DataRegister); - /* Preamble1 */ if (mstp_port->DataRegister == 0x55) { + /* Preamble1 */ /* receive the remainder of the frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE; - } - /* EatAnOctet */ - else { + } else { + /* EatAnOctet */ printf_receive_data("\n"); /* wait for the start of a frame. */ } @@ -374,17 +348,17 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); } break; - /* In the PREAMBLE state, the node waits for the second octet of the - * preamble. */ 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) > Tframe_abort) { + 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; - } - /* Error */ - else if (mstp_port->ReceiveError == true) { + } else if (mstp_port->ReceiveError == true) { + /* Error */ mstp_port->ReceiveError = false; mstp_port->SilenceTimerReset((void *)mstp_port); INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); @@ -392,19 +366,17 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } else if (mstp_port->DataAvailable == true) { printf_receive_data("%02X ", mstp_port->DataRegister); - /* Preamble2 */ 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; - } - /* ignore RepeatedPreamble1 */ - else if (mstp_port->DataRegister == 0x55) { + } else if (mstp_port->DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ /* wait for the second preamble octet. */ - } - /* NotPreamble */ - else { + } else { + /* NotPreamble */ /* wait for the start of a frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } @@ -413,11 +385,12 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount); } break; - /* In the HEADER state, the node waits for the fixed message header. - */ 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) > Tframe_abort) { + 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; @@ -425,10 +398,9 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) 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), - Tframe_abort); - } - /* Error */ - else if (mstp_port->ReceiveError == true) { + 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); @@ -440,43 +412,38 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } else if (mstp_port->DataAvailable == true) { printf_receive_data("%02X ", mstp_port->DataRegister); - /* FrameType */ 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; - } - /* Destination */ - else if (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; - } - /* Source */ - else if (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; - } - /* Length1 */ - else if (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; - } - /* Length2 */ - else if (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; - } - /* HeaderCRC */ - else if (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; @@ -496,18 +463,9 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) printf_receive_data("%s", mstptext_frame_type( (unsigned)mstp_port->FrameType)); - if ((mstp_port->DestinationAddress == - mstp_port->This_Station) || - (mstp_port->DestinationAddress == - MSTP_BROADCAST_ADDRESS)) { - /* ForUs */ - /* indicate that a frame with no data has been - * received */ - mstp_port->ReceivedValidFrame = true; - } else { - /* NotForUs */ - mstp_port->ReceivedValidFrameNotForUs = true; - } + /* indicate that a frame with no data has been + * received */ + mstp_port->ReceivedValidFrame = true; /* wait for the start of the next frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; } else { @@ -538,9 +496,8 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->DataCRC = 0xFFFF; } } - } - /* not per MS/TP standard, but it is a case not covered */ - else { + } 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 */ @@ -555,24 +512,24 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->DataAvailable = false; } break; - /* In the DATA state, the node waits for the data portion of a - * frame. */ case MSTP_RECEIVE_STATE_DATA: case MSTP_RECEIVE_STATE_SKIP_DATA: - /* Timeout */ - if (mstp_port->SilenceTimer((void *)mstp_port) > Tframe_abort) { + /* 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), - Tframe_abort); + mstp_port->Tframe_abort); /* wait for the start of the next frame. */ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE; - } - /* Error */ - else if (mstp_port->ReceiveError == true) { + } 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 @@ -615,36 +572,23 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) 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) && + if (((mstp_port->Index + 1) < mstp_port->InputBufferSize) && (mstp_port->FrameType >= Nmin_COBS_type) && (mstp_port->FrameType <= Nmax_COBS_type)) { if (cobs_frame_decode( &mstp_port->InputBuffer[mstp_port->Index + 1], mstp_port->InputBufferSize, mstp_port->InputBuffer, mstp_port->Index + 1)) { - if (mstp_port->receive_state == - MSTP_RECEIVE_STATE_DATA) { - /* ForUs */ - mstp_port->ReceivedValidFrame = true; - } else { - /* NotForUs */ - mstp_port->ReceivedValidFrameNotForUs = true; - } + mstp_port->ReceivedValidFrame = true; } else { mstp_port->ReceivedInvalidFrame = true; } } else { /* STATE DATA CRC - no need for new state */ - /* indicate the complete reception of a valid frame */ if (mstp_port->DataCRC == 0xF0B8) { - if (mstp_port->receive_state == - MSTP_RECEIVE_STATE_DATA) { - /* ForUs */ - mstp_port->ReceivedValidFrame = true; - } else { - /* NotForUs */ - mstp_port->ReceivedValidFrameNotForUs = true; - } + /* indicate the complete reception of a + valid frame */ + mstp_port->ReceivedValidFrame = true; } else { mstp_port->ReceivedInvalidFrame = true; printf_receive_error( @@ -674,8 +618,12 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) return; } -/* returns true if we need to transition immediately */ -bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) +/** + * @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; @@ -695,16 +643,34 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1); switch (mstp_port->master_state) { case MSTP_MASTER_STATE_INITIALIZE: - /* 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; + 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; + mstp_port->Poll_Station = (mstp_port->Next_Station + 1) % + (mstp_port->Zero_Config_Max_Master + 1); + mstp_port->TokenCount = Npoll; + 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. */ @@ -721,8 +687,17 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->DataLength, mstp_port->FrameCount, mstp_port->SilenceTimer((void *)mstp_port), mstptext_frame_type((unsigned)mstp_port->FrameType)); - if ((mstp_port->DestinationAddress == - mstp_port->This_Station) || + if (mstp_port->SourceAddress == mstp_port->This_Station) { + /* DuplicateNode */ + if (mstp_port->ZeroConfigEnabled) { + /* we are a zero config node - start over */ + mstp_port->Zero_Config_State = + MSTP_ZERO_CONFIG_STATE_INIT; + mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE; + } + mstp_port->ReceivedValidFrame = false; + } else if ((mstp_port->DestinationAddress == + mstp_port->This_Station) || (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) { /* destined for me! */ switch (mstp_port->FrameType) { @@ -857,7 +832,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) 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) >= Treply_timeout) { + 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; @@ -1011,7 +987,8 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) 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) <= Tusage_timeout) { + 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. @@ -1138,7 +1115,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) } mstp_port->ReceivedValidFrame = false; } else if ((mstp_port->SilenceTimer((void *)mstp_port) > - Tusage_timeout) || + mstp_port->Tusage_timeout) || (mstp_port->ReceivedInvalidFrame == true)) { if (mstp_port->SoleMaster == true) { /* SoleMaster */ @@ -1209,7 +1186,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) /* clear our flag we were holding for comparison */ mstp_port->ReceivedValidFrame = false; } else if (mstp_port->SilenceTimer((void *)mstp_port) > - Treply_delay) { + mstp_port->Treply_delay) { /* DeferredReply */ /* If no reply will be available from the higher layers */ /* within Treply_delay after the reception of the */ @@ -1247,7 +1224,11 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) return transition_now; } -void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) +/** + * @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; @@ -1257,45 +1238,15 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) /* 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: if (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS) { - /* 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, - (uint8_t *)&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) > - 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; - } - } else { - mstp_port->ReceivedValidFrame = false; + /* indicate successful reception to the higher layers */ + (void)MSTP_Put_Receive(mstp_port); } break; case FRAME_TYPE_TEST_REQUEST: - mstp_port->ReceivedValidFrame = false; 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); @@ -1305,9 +1256,352 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) case FRAME_TYPE_TEST_RESPONSE: case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: default: - mstp_port->ReceivedValidFrame = false; 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, + (uint8_t *)&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 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; + mstp_port->Zero_Config_Station = Nmin_poll_station; + mstp_port->Npoll_slot = 1 + (rand() % 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_Zero_Config_UUID_Init(mstp_port); + 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) { + /* ClaimAddress */ + /* 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++; + if (mstp_port->Zero_Config_Station > Nmax_poll_station) { + /* start again from first */ + mstp_port->Zero_Config_Station = Nmin_poll_station; + } + mstp_port->Poll_Count = 0; + } else if ((frame == FRAME_TYPE_POLL_FOR_MASTER) && + (dst == mstp_port->Zero_Config_Station)) { + mstp_port->Poll_Count++; + /* calculate this node poll count priority */ + count = Nmin_poll + mstp_port->Npoll_slot; + if (mstp_port->Poll_Count == count) { + /* ClaimAddress */ + 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 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) { + 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++; + if (mstp_port->Zero_Config_Station > Nmax_poll_station) { + /* start again from first */ + mstp_port->Zero_Config_Station = Nmin_poll_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) { + /* ClaimLostToken */ + 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++; + if (mstp_port->Zero_Config_Station > Nmax_poll_station) { + /* start again from first */ + mstp_port->Zero_Config_Station = Nmin_poll_station; + } + mstp_port->Zero_Config_State = MSTP_ZERO_CONFIG_STATE_LURK; + } + } 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; } } @@ -1317,7 +1611,7 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) /* 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(volatile struct mstp_port_struct_t *mstp_port) +void MSTP_Init(struct mstp_port_struct_t *mstp_port) { if (mstp_port) { #if 0 @@ -1330,10 +1624,28 @@ void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port) /* 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; /* FIXME: point to functions */ mstp_port->SilenceTimer = Timer_Silence; - mstp_port = >SilenceTimerReset = Timer_Silence_Reset; + mstp_port->SilenceTimerReset = Timer_Silence_Reset; #endif + if ((mstp_port->Tframe_abort < 6) || (mstp_port->Tframe_abort > 100)) { + mstp_port->Tframe_abort = DEFAULT_Tframe_abort; + } + if (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; @@ -1351,11 +1663,12 @@ void MSTP_Init(volatile struct mstp_port_struct_t *mstp_port) mstp_port->Poll_Station = mstp_port->This_Station; mstp_port->ReceivedInvalidFrame = false; mstp_port->ReceivedValidFrame = false; - mstp_port->ReceivedValidFrameNotForUs = false; mstp_port->RetryCount = 0; - mstp_port->SilenceTimerReset((void *)mstp_port); + 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; } } diff --git a/src/bacnet/datalink/mstp.h b/src/bacnet/datalink/mstp.h index 1d2a9a8a..2e30d50e 100644 --- a/src/bacnet/datalink/mstp.h +++ b/src/bacnet/datalink/mstp.h @@ -1,27 +1,27 @@ /************************************************************************** -* -* Copyright (C) 2004 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -* -*********************************************************************/ + * + * Copyright (C) 2004 Steve Karg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + *********************************************************************/ #ifndef MSTP_H #define MSTP_H @@ -30,6 +30,17 @@ #include #include "bacnet/bacnet_stack_exports.h" #include "bacnet/datalink/mstpdef.h" +#include "bacnet/config.h" + +/* Repeater turnoff delay. The duration of a continuous logical one state */ +/* at the active input port of an MS/TP repeater after which the repeater */ +/* will enter the IDLE state: 29 bit times < Troff < 40 bit times. */ +#ifndef Troff +#define Troff 30 +#endif + +/* size of the buffer used to send and validate a unique test request */ +#define MSTP_UUID_SIZE 16 struct mstp_port_struct_t { MSTP_RECEIVE_STATE receive_state; @@ -39,21 +50,21 @@ struct mstp_port_struct_t { /* A Boolean flag set to TRUE by the Receive State Machine */ /* if an error is detected during the reception of a frame. */ /* Set to FALSE by the Master or Slave Node state machine. */ - unsigned ReceiveError:1; + unsigned ReceiveError : 1; /* There is data in the buffer */ - unsigned DataAvailable:1; - unsigned ReceivedInvalidFrame:1; + unsigned DataAvailable : 1; + 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 Master or Slave Node state machine. */ - unsigned ReceivedValidFrame:1; - /* A Boolean flag set to TRUE by the Receive State Machine */ - /* if a valid frame is received but it is not addressed to us. */ - /* Set to FALSE by the Master or Slave Node state machine. */ - 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; + unsigned ReceivedValidFrame : 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 to TRUE if this node is a slave node */ + unsigned SlaveNodeEnabled : 1; + /* A Boolean flag set to TRUE if this node is using a ZeroConfig address */ + unsigned ZeroConfigEnabled : 1; /* stores the latest received data */ uint8_t DataRegister; /* Used to accumulate the CRC on the data field of a frame. */ @@ -65,185 +76,227 @@ struct mstp_port_struct_t { uint16_t DataLength; /* Used to store the destination address of a received frame. */ 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 */ + /* Used to count the number of received octets or errors. + This is used in the detection of link activity. + Compared to Nmin_octets */ uint8_t EventCount; /* Used to store the frame type of a received frame. */ uint8_t FrameType; - /* 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 */ + /* 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. */ uint8_t FrameCount; /* Used to accumulate the CRC on the header of a frame. */ uint8_t HeaderCRC; /* Used to store the actual CRC from the header. */ uint8_t HeaderCRCActual; - /* Used as an index by the Receive State Machine, up to a maximum value of */ - /* InputBufferSize. */ + /* Used as an index by the Receive State Machine, + up to a maximum value of InputBufferSize. */ uint32_t Index; - /* An array of octets, used to store octets as they are received. */ - /* InputBuffer is indexed from 0 to InputBufferSize-1. */ - /* The maximum size of a frame is 501 octets. */ - /* FIXME: assign this to an actual array of bytes! */ + /* An array of octets, used to store octets as they are received. + InputBuffer is indexed from 0 to InputBufferSize-1. */ + /* Note: 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. */ uint8_t *InputBuffer; uint16_t InputBufferSize; - /* "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 */ + /* "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. */ uint8_t Next_Station; - /* "Poll Station," the MAC address of the node to which This Station last */ - /* sent a Poll For Master. This is used during token maintenance. */ + /* "Poll Station," the MAC address of the node to which This Station last + sent a Poll For Master. This is used during token maintenance. */ uint8_t Poll_Station; - /* A counter of transmission retries used for Token and Poll For Master */ - /* transmission. */ + /* A counter of transmission retries used for Token and Poll For Master + transmission. */ unsigned RetryCount; - /* A timer with nominal 5 millisecond resolution used to measure and */ - /* generate silence on the medium between octets. It is incremented by a */ - /* timer process and is cleared by the Receive State Machine when activity */ - /* is detected and by the SendFrame procedure as each octet is transmitted. */ - /* Since the timer resolution is limited and the timer is not necessarily */ - /* synchronized to other machine events, a timer value of N will actually */ - /* denote intervals between N-1 and N */ + /* A timer with nominal 5 millisecond resolution used to measure + and generate silence on the medium between octets. It is + incremented by a timer process and is cleared by the Receive + State Machine when activity is detected and by the SendFrame + procedure as each octet is transmitted. */ + /* Since the timer resolution is limited and the timer is not necessarily + synchronized to other machine events, a timer value of N will actually + denote intervals between N-1 and N */ /* Note: done here as functions - put into timer task or ISR so that you can be atomic on 8 bit microcontrollers */ - uint32_t( - *SilenceTimer) ( - void *pArg); - void ( - *SilenceTimerReset) ( - void *pArg); + uint32_t (*SilenceTimer)(void *pArg); + void (*SilenceTimerReset)(void *pArg); - /* A timer used to measure and generate Reply Postponed frames. It is */ - /* incremented by a timer process and is cleared by the Master Node State */ - /* Machine when a Data Expecting Reply Answer activity is completed. */ - /* note: we always send a reply postponed since a message other than + /* A timer used to measure and generate Reply Postponed frames. It is + incremented by a timer process and is cleared by the Master Node State + Machine when a Data Expecting Reply Answer activity is completed. + note: we always send a reply postponed since a message other than the reply may be in the transmit queue */ /* uint16_t ReplyPostponedTimer; */ /* Used to store the Source Address of a received frame. */ uint8_t SourceAddress; - /* 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. */ + /* 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. */ unsigned TokenCount; - /* "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. */ + /* "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. */ 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. */ + /* 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.*/ uint8_t 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. */ + /* 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. */ uint8_t Nmax_master; - /* An array of octets, used to store octets for transmitting */ - /* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ - /* The maximum size of a frame is 501 octets. */ - /* FIXME: assign this to an actual array of bytes! */ - /* Note: the buffer is designed as a pointer since some compilers + /* An array of octets, used to store octets for transmitting + OutputBuffer is indexed from 0 to OutputBufferSize-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. */ uint8_t *OutputBuffer; uint16_t OutputBufferSize; + /* orderly transition tracking for zero-configuration node startup */ + MSTP_ZERO_CONFIG_STATE Zero_Config_State; + /* the MAC address that this node is testing for MAC addresses + that are not in-use.*/ + uint8_t Zero_Config_Station; + /* Used to count the number of received poll-for-master frames + This is used in the detection of addresses not in-use. */ + uint8_t Poll_Count; + /* This parameter is random value 1..64, used to choose the poll slot */ + uint8_t Npoll_slot; + /* UUID for storing the unique identifier for this node + used to send and validate a unique test request and response */ + uint8_t UUID[MSTP_UUID_SIZE]; + /* amount of silence time to wait, in milliseconds */ + uint32_t Zero_Config_Silence; + /* This parameter tracks the highest polled station address. + The value of this parameter shall be less than or equal to 127. + In the absence of other fixed address nodes, this value shall be 127. */ + uint8_t Zero_Config_Max_Master; + + /* 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. + Tframe_abort = 1 + ((60*1000UL)/RS485_Baud); */ + uint8_t Tframe_abort; + + /* 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. */ + uint8_t Treply_delay; + + /* 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.) */ + uint16_t Treply_timeout; + + /* The minimum 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.) */ + uint8_t Tusage_timeout; + + /* The minimum time after the end of the stop bit of the final + octet of a received frame before a node may enable its + EIA-485 driver: 40 bit times. + 40 bits is 4 octets including a start and stop bit with each octet. + turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + uint8_t Tturnaround_timeout; + /*Platform-specific port data */ void *UserData; - }; #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - BACNET_STACK_EXPORT - void MSTP_Init( - volatile struct mstp_port_struct_t *mstp_port); - BACNET_STACK_EXPORT - void MSTP_Receive_Frame_FSM( - volatile struct mstp_port_struct_t - *mstp_port); - BACNET_STACK_EXPORT - bool MSTP_Master_Node_FSM( - volatile struct mstp_port_struct_t - *mstp_port); - BACNET_STACK_EXPORT - void MSTP_Slave_Node_FSM( - volatile struct mstp_port_struct_t *mstp_port); +BACNET_STACK_EXPORT +void MSTP_Init(struct mstp_port_struct_t *mstp_port); +BACNET_STACK_EXPORT +void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port); +BACNET_STACK_EXPORT +bool MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port); +BACNET_STACK_EXPORT +void MSTP_Slave_Node_FSM(struct mstp_port_struct_t *mstp_port); - /* returns true if line is active */ - BACNET_STACK_EXPORT - bool MSTP_Line_Active( - volatile struct mstp_port_struct_t *mstp_port); +/* returns true if line is active */ +BACNET_STACK_EXPORT +bool MSTP_Line_Active(struct mstp_port_struct_t *mstp_port); - BACNET_STACK_EXPORT - uint16_t MSTP_Create_Frame( - uint8_t * buffer, /* where frame is loaded */ - uint16_t buffer_len, /* amount of space available */ - uint8_t frame_type, /* type of frame to send - see defines */ - uint8_t destination, /* destination address */ - uint8_t source, /* source address */ - uint8_t * data, /* any data to be sent - may be null */ - uint16_t data_len); /* number of bytes of data (up to 501) */ +BACNET_STACK_EXPORT +uint16_t MSTP_Create_Frame(uint8_t *buffer, + uint16_t buffer_len, + uint8_t frame_type, + uint8_t destination, + uint8_t source, + uint8_t *data, + uint16_t data_len); - BACNET_STACK_EXPORT - void MSTP_Create_And_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ - uint8_t frame_type, /* type of frame to send - see defines */ - uint8_t destination, /* destination address */ - uint8_t source, /* source address */ - uint8_t * data, /* any data to be sent - may be null */ - uint16_t data_len); +BACNET_STACK_EXPORT +void MSTP_Create_And_Send_Frame( + struct mstp_port_struct_t *mstp_port, + uint8_t frame_type, + uint8_t destination, + uint8_t source, + uint8_t *data, + uint16_t data_len); - BACNET_STACK_EXPORT - void MSTP_Fill_BACnet_Address( - BACNET_ADDRESS * src, - uint8_t mstp_address); +BACNET_STACK_EXPORT +void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address); - /* functions used by the MS/TP state machine to put or get data */ - /* FIXME: developer must implement these in their DLMSTP module */ +BACNET_STACK_EXPORT +void MSTP_Zero_Config_UUID_Init(struct mstp_port_struct_t *mstp_port); - BACNET_STACK_EXPORT - uint16_t MSTP_Put_Receive( - volatile struct mstp_port_struct_t *mstp_port); +BACNET_STACK_EXPORT +void MSTP_Zero_Config_FSM(struct mstp_port_struct_t *mstp_port); - /* for the MS/TP state machine to use for getting data to send */ - /* Return: amount of PDU data */ - BACNET_STACK_EXPORT - uint16_t MSTP_Get_Send( - volatile struct mstp_port_struct_t *mstp_port, - unsigned timeout); /* milliseconds to wait for a packet */ - /* for the MS/TP state machine to use for getting the reply for - Data-Expecting-Reply Frame */ - /* Return: amount of PDU data */ - BACNET_STACK_EXPORT - uint16_t MSTP_Get_Reply( - volatile struct mstp_port_struct_t *mstp_port, - unsigned timeout); /* milliseconds to wait for a packet */ +/* functions used by the MS/TP state machine to put or get data */ +/* FIXME: developer must implement these in their DLMSTP module */ - BACNET_STACK_EXPORT - void MSTP_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, - uint8_t * buffer, - uint16_t nbytes); +BACNET_STACK_EXPORT +uint16_t MSTP_Put_Receive( + struct mstp_port_struct_t *mstp_port); + +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +BACNET_STACK_EXPORT +uint16_t MSTP_Get_Send(struct mstp_port_struct_t *mstp_port, + unsigned timeout); /* milliseconds to wait for a packet */ +/* for the MS/TP state machine to use for getting the reply for + Data-Expecting-Reply Frame */ +/* Return: amount of PDU data */ +BACNET_STACK_EXPORT +uint16_t MSTP_Get_Reply(struct mstp_port_struct_t *mstp_port, + unsigned timeout); /* milliseconds to wait for a packet */ + +BACNET_STACK_EXPORT +void MSTP_Send_Frame( + struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes); #ifdef __cplusplus } diff --git a/src/bacnet/datalink/mstpdef.h b/src/bacnet/datalink/mstpdef.h index aadf8333..e865e0a0 100644 --- a/src/bacnet/datalink/mstpdef.h +++ b/src/bacnet/datalink/mstpdef.h @@ -1,26 +1,26 @@ /************************************************************************** -* -* Copyright (C) 2012 Steve Karg -* -* Permission is hereby granted, free of charge, to any person obtaining -* a copy of this software and associated documentation files (the -* "Software"), to deal in the Software without restriction, including -* without limitation the rights to use, copy, modify, merge, publish, -* distribute, sublicense, and/or sell copies of the Software, and to -* permit persons to whom the Software is furnished to do so, subject to -* the following conditions: -* -* The above copyright notice and this permission notice shall be included -* in all copies or substantial portions of the Software. -* -* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*********************************************************************/ + * + * Copyright (C) 2012 Steve Karg + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + *********************************************************************/ #ifndef MSTPDEF_H #define MSTPDEF_H @@ -32,7 +32,7 @@ /* The value 255 is used to denote broadcast when used as a */ /* destination address but is not allowed as a value for a station. */ /* Station addresses for master nodes can be 0-127. */ -/* Station addresses for slave nodes can be 127-254. */ +/* Station addresses for slave nodes can be 0-254. */ #define MSTP_BROADCAST_ADDRESS 255 /* MS/TP Frame Type */ @@ -52,15 +52,23 @@ /* These frames are available to vendors as proprietary (non-BACnet) frames. */ /* The first two octets of the Data field shall specify the unique vendor */ /* identification code, most significant octet first, for the type of */ -/* vendor-proprietary frame to be conveyed. The length of the data portion */ -/* of a Proprietary frame shall be in the range of 2 to 501 octets. */ +/* vendor-proprietary frame to be conveyed. */ #define FRAME_TYPE_PROPRIETARY_MIN 128 #define FRAME_TYPE_PROPRIETARY_MAX 255 /* The initial CRC16 checksum value */ #define CRC16_INITIAL_VALUE (0xFFFF) #define CRC32K_INITIAL_VALUE (0xFFFFFFFF) #define CRC32K_RESIDUE (0x0843323B) +/* frame specific data */ #define MSTP_PREAMBLE_X55 (0x55) +/* The length of the data portion of a Test_Request, Test_Response, + BACnet Data Expecting Reply, or BACnet Data Not Expecting Reply frame + may range from 0 to 501 octets. + The length of the data portion of a proprietary frame shall + be in the range of 2 to 501 octets.*/ +#define MSTP_FRAME_NPDU_MAX 501 +/* COBS-encoded frames data parameter length is between + 502 and 1497 octets, inclusive */ #define MSTP_EXTENDED_FRAME_NPDU_MAX 1497 /* receive FSM states */ @@ -85,6 +93,16 @@ typedef enum { MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 } MSTP_MASTER_STATE; +/* MSTP zero config FSM states */ +typedef enum MSTP_Zero_Config_State { + MSTP_ZERO_CONFIG_STATE_INIT = 0, + MSTP_ZERO_CONFIG_STATE_IDLE = 1, + MSTP_ZERO_CONFIG_STATE_LURK = 2, + MSTP_ZERO_CONFIG_STATE_CLAIM = 3, + MSTP_ZERO_CONFIG_STATE_CONFIRM = 4, + MSTP_ZERO_CONFIG_STATE_USE = 5 +} MSTP_ZERO_CONFIG_STATE; + /* The time without a DataAvailable or ReceiveError event before declaration */ /* of loss of token: 500 milliseconds. */ #define Tno_token 500 @@ -98,13 +116,29 @@ typedef enum { /* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ /* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ /* 40 bits is 4 octets including a start and stop bit with each octet */ -#define Tturnaround (40UL) +#define Tturnaround (40UL) /* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ /* The number of tokens received or used before a Poll For Master cycle */ /* is executed: 50. */ #define Npoll 50 +/* The minimum number of polls received before a zero-config address */ +/* is claimed: 8. */ +#define Nmin_poll 8 + +/* The first zero-config address: 64 */ +#define Nmin_poll_station 64 + +/* The last zero-config address: 127 */ +#define Nmax_poll_station 127 + +/* The number of zero-config station poll slots: 64 */ +#define Nmax_poll_slot 64 + +/* The last master node address: 127 */ +#define Nmax_master_station 127 + /* The number of retries on sending Token: 1. */ #define Nretry_token 1 @@ -126,6 +160,15 @@ typedef enum { /* 15 milliseconds. */ #define Tusage_delay 15 +/* 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 + +#define DEFAULT_Tframe_abort 95 +#define DEFAULT_Treply_delay 245 +#define DEFAULT_Treply_timeout 250 +#define DEFAULT_Tusage_timeout 35 + #define DEFAULT_MAX_INFO_FRAMES 1 #define DEFAULT_MAX_MASTER 127 #define DEFAULT_MAC_ADDRESS 127 @@ -134,7 +177,6 @@ typedef enum { extern "C" { #endif /* __cplusplus */ - #ifdef __cplusplus } #endif /* __cplusplus */ diff --git a/src/bacnet/datalink/mstptext.c b/src/bacnet/datalink/mstptext.c index 71330a85..e97119a7 100644 --- a/src/bacnet/datalink/mstptext.c +++ b/src/bacnet/datalink/mstptext.c @@ -87,3 +87,18 @@ const char *mstptext_frame_type(unsigned index) return indtext_by_index_split_default(mstp_frame_type_text, index, FRAME_TYPE_PROPRIETARY_MIN, "UNKNOWN", "PROPRIETARY"); } + +static INDTEXT_DATA mstp_zero_config_state_text[] = { + { MSTP_ZERO_CONFIG_STATE_INIT, "INIT" }, + { MSTP_ZERO_CONFIG_STATE_IDLE, "IDLE" }, + { MSTP_ZERO_CONFIG_STATE_LURK, "LURK" }, + { MSTP_ZERO_CONFIG_STATE_CLAIM, "CLAIM" }, + { MSTP_ZERO_CONFIG_STATE_CONFIRM, "CONFIRM" }, + { MSTP_ZERO_CONFIG_STATE_USE, "USE" }, + { 0, NULL } + }; + +const char *mstptext_zero_config_state(unsigned index) +{ + return indtext_by_index_default(mstp_zero_config_state_text, index, "unknown"); +} diff --git a/src/bacnet/datalink/mstptext.h b/src/bacnet/datalink/mstptext.h index b25e418a..6e71b3c7 100644 --- a/src/bacnet/datalink/mstptext.h +++ b/src/bacnet/datalink/mstptext.h @@ -39,6 +39,9 @@ extern "C" { BACNET_STACK_EXPORT const char *mstptext_frame_type( unsigned index); + BACNET_STACK_EXPORT + const char *mstptext_zero_config_state( + unsigned index); #ifdef __cplusplus } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 002ea864..4d79cb49 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -158,6 +158,7 @@ list(APPEND testdirs bacnet/datalink/cobs bacnet/datalink/crc bacnet/datalink/bvlc + bacnet/datalink/mstp ) enable_testing() diff --git a/test/bacnet/datalink/mstp/CMakeLists.txt b/test/bacnet/datalink/mstp/CMakeLists.txt new file mode 100644 index 00000000..b4d63ae4 --- /dev/null +++ b/test/bacnet/datalink/mstp/CMakeLists.txt @@ -0,0 +1,54 @@ +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10 FATAL_ERROR) + +get_filename_component(basename ${CMAKE_CURRENT_SOURCE_DIR} NAME) +project(test_${basename} + VERSION 1.0.0 + LANGUAGES C) + + +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/src" + SRC_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +string(REGEX REPLACE + "/test/bacnet/[a-zA-Z_/-]*$" + "/test" + TST_DIR + ${CMAKE_CURRENT_SOURCE_DIR}) +set(ZTST_DIR "${TST_DIR}/ztest/src") + +add_compile_definitions( + BIG_ENDIAN=0 + CONFIG_ZTEST=1 + BACDL_MSTP=1 + ) + +include_directories( + ${SRC_DIR} + ${TST_DIR}/ztest/include + ) + +add_executable(${PROJECT_NAME} + # File(s) under test + ${SRC_DIR}/bacnet/datalink/mstp.c + ${SRC_DIR}/bacnet/datalink/mstptext.c + ${SRC_DIR}/bacnet/datalink/crc.c + ${SRC_DIR}/bacnet/datalink/cobs.c + ${SRC_DIR}/bacnet/basic/sys/fifo.c + ${SRC_DIR}/bacnet/indtext.c + # core files needed + ${SRC_DIR}/bacnet/bacdcode.c + ${SRC_DIR}/bacnet/bacint.c + ${SRC_DIR}/bacnet/bacstr.c + ${SRC_DIR}/bacnet/bacreal.c + ${SRC_DIR}/bacnet/npdu.c + ${SRC_DIR}/bacnet/basic/sys/bigend.c + # Support files and stubs (pathname alphabetical) + # Test and test library files + ./src/main.c + ${ZTST_DIR}/ztest_mock.c + ${ZTST_DIR}/ztest.c + ) diff --git a/test/bacnet/datalink/mstp/src/main.c b/test/bacnet/datalink/mstp/src/main.c new file mode 100644 index 00000000..c4c0cb2e --- /dev/null +++ b/test/bacnet/datalink/mstp/src/main.c @@ -0,0 +1,1068 @@ +/** + * @file + * @author Steve Karg + * @brief test BACnet MSTP datalink state machines + * + * SPDX-License-Identifier: MIT + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/** + * @addtogroup bacnet_tests + * @{ + */ + +#define INCREMENT_AND_LIMIT_UINT8(x) \ + { \ + if ((x) < 0xFF) \ + (x)++; \ + } + +static uint8_t RxBuffer[MAX_MPDU]; +static uint8_t TxBuffer[MAX_MPDU]; +/* test stub functions */ + +/** + * @brief Send a frame to the RS-485 network + * @param mstp_port port specific context data + * @param buffer pointer to the frame data + * @param nbytes number of bytes to send + */ +void RS485_Send_Frame( + struct mstp_port_struct_t *mstp_port, uint8_t *buffer, uint16_t nbytes) +{ + (void)mstp_port; + (void)buffer; + (void)nbytes; +} + +/* A data queue for the test */ +FIFO_DATA_STORE(Test_Queue_Data, MAX_MPDU); +static FIFO_BUFFER Test_Queue; + +/** + * @brief Load the input buffer with data + * @param buffer pointer to the data + * @param len number of bytes to load + */ +static void Load_Input_Buffer(uint8_t *buffer, size_t len) +{ + static bool initialized = false; /* tracks our init */ + if (!initialized) { + initialized = true; + FIFO_Init(&Test_Queue, Test_Queue_Data, MAX_MPDU); + } + /* empty any the existing data */ + FIFO_Flush(&Test_Queue); + FIFO_Add(&Test_Queue, buffer, len); +} + +/** + * @brief Check the UART for data + * @param mstp_port port specific context data + */ +void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port) +{ + if (!FIFO_Empty(&Test_Queue)) { + if (mstp_port) { + mstp_port->DataRegister = FIFO_Get(&Test_Queue); + mstp_port->DataAvailable = true; + } + } +} + +/** + * @brief Store data about a RS-485 network received packet + * @param mstp_port port specific context data + */ +uint16_t MSTP_Put_Receive(struct mstp_port_struct_t *mstp_port) +{ + return mstp_port->DataLength; +} + +/** + * @brief MS/TP state machine calls this to get data to send + * @param mstp_port port specific context data + * @param timeout milliseconds to wait for a packet to send + * @return amount of PDU data + */ +uint16_t MSTP_Get_Send(struct mstp_port_struct_t *mstp_port, unsigned timeout) +{ /* milliseconds to wait for a packet */ + return 0; +} + +/** + * @brief MS/TP state machine calls this to get data to send + * @param mstp_port port specific context data + * @param timeout milliseconds to wait for a packet + * @return amount of PDU data + */ +uint16_t MSTP_Get_Reply(struct mstp_port_struct_t *mstp_port, unsigned timeout) +{ /* milliseconds to wait for a packet */ + return 0; +} + +/* track the RS485 line silence time in milliseconds */ +uint32_t SilenceTime = 0; +/** + * @brief MS/TP state machine calls this to get the silence time + * @param pArg pointer to the port specific context data + * @return amount of time in milliseconds + */ +static uint32_t Timer_Silence(void *pArg) +{ + (void)pArg; + return SilenceTime; +} + +/** + * @brief MS/TP state machine calls this to reset the silence time + * @param pArg pointer to the port specific context data + */ +static void Timer_Silence_Reset(void *pArg) +{ + (void)pArg; + SilenceTime = 0; +} + +/** + * @brief MS/TP state machine calls this to send a frame + * @param mstp_port port specific context data + * @param buffer pointer to the frame data + * @param nbytes number of bytes to send + */ +void MSTP_Send_Frame( + struct mstp_port_struct_t *mstp_port, uint8_t *buffer, uint16_t nbytes) +{ + if (mstp_port && mstp_port->OutputBuffer && buffer && (nbytes > 0) && + (nbytes <= mstp_port->OutputBufferSize)) { + memcpy(mstp_port->OutputBuffer, buffer, nbytes); + } +} + +static void testReceiveNodeFSM(void) +{ + struct mstp_port_struct_t mstp_port; /* port data */ + unsigned EventCount = 0; /* local counter */ + uint8_t my_mac = 0x05; /* local MAC address */ + uint8_t HeaderCRC = 0; /* for local CRC calculation */ + uint8_t FrameType = 0; /* type of packet that was sent */ + unsigned len; /* used for the size of the message packet */ + size_t i; /* used to loop through the message bytes */ + uint8_t buffer[MAX_MPDU] = { 0 }; + uint8_t data[MAX_PDU] = { 0 }; + uint8_t data_proprietary[MSTP_FRAME_NPDU_MAX] = { 0 }; + + mstp_port.InputBuffer = &RxBuffer[0]; + mstp_port.InputBufferSize = sizeof(RxBuffer); + mstp_port.OutputBuffer = &TxBuffer[0]; + mstp_port.OutputBufferSize = sizeof(TxBuffer); + mstp_port.SilenceTimer = Timer_Silence; + mstp_port.SilenceTimerReset = Timer_Silence_Reset; + mstp_port.This_Station = my_mac; + mstp_port.Nmax_info_frames = 1; + mstp_port.Nmax_master = 127; + MSTP_Init(&mstp_port); + /* check the receive error during idle */ + mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE; + mstp_port.ReceiveError = true; + SilenceTime = 255; + mstp_port.EventCount = 0; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.ReceiveError == false, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for bad packet header */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for good packet header, but timeout */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + /* force the timeout */ + SilenceTime = mstp_port.Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for good packet header preamble, but receive error */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.ReceiveError == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for good packet header preamble1, but bad preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + /* repeated preamble1 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + /* bad data */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x11; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.ReceiveError == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for good packet header preamble, but timeout in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 0, NULL); + zassert_true(mstp_port.HeaderCRC == 0xFF, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + /* force the timeout */ + SilenceTime = mstp_port.Tframe_abort + 1; + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + zassert_true(mstp_port.ReceivedInvalidFrame == true, NULL); + /* check for good packet header preamble, but error in packet */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 0, NULL); + zassert_true(mstp_port.HeaderCRC == 0xFF, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + /* force the error */ + mstp_port.ReceiveError = true; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.ReceiveError == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* check for good packet header preamble */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x55; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + /* preamble2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0xFF; + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 0, NULL); + zassert_true(mstp_port.HeaderCRC == 0xFF, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + /* no change of state if no data yet */ + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + /* Data is received - index is incremented */ + /* FrameType */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = FRAME_TYPE_TOKEN; + HeaderCRC = 0xFF; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 1, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + zassert_true(FrameType == FRAME_TYPE_TOKEN, NULL); + /* Destination */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0x10; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 2, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + zassert_true(mstp_port.DestinationAddress == 0x10, NULL); + /* Source */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = my_mac; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 3, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + zassert_true(mstp_port.SourceAddress == my_mac, NULL); + /* Length1 = length*256 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 4, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + zassert_true(mstp_port.DataLength == 0, NULL); + /* Length2 */ + mstp_port.DataAvailable = true; + mstp_port.DataRegister = 0; + HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister, HeaderCRC); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 5, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER, NULL); + zassert_true(mstp_port.DataLength == 0, NULL); + /* HeaderCRC */ + mstp_port.DataAvailable = true; + zassert_true(HeaderCRC == 0x73, NULL); /* per Annex G example */ + mstp_port.DataRegister = ~HeaderCRC; /* one's compliment of CRC is sent */ + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + zassert_true(mstp_port.Index == 5, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + zassert_true(mstp_port.HeaderCRC == 0x55, NULL); + /* BadCRC in header check */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, + 0x10, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + zassert_true(len > 0, NULL); + /* make the header CRC bad */ + buffer[7] = 0x00; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, "i=%u %u!=%u len=%u", + i, mstp_port.EventCount, EventCount, len); + } + zassert_true(mstp_port.ReceivedInvalidFrame == true, NULL); + zassert_true(mstp_port.ReceivedValidFrame == false, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* NoData for us */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, + my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + zassert_true(len > 0, NULL); + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + } + zassert_true(mstp_port.ReceivedInvalidFrame == false, NULL); + zassert_true(mstp_port.ReceivedValidFrame == true, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* FrameTooLong */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_TOKEN, + my_mac, /* destination */ + my_mac, /* source */ + NULL, /* data */ + 0); /* data size */ + zassert_true(len > 0, NULL); + /* make the header data length bad */ + buffer[5] = 0x02; + Load_Input_Buffer(buffer, len); + for (i = 0; i < len; i++) { + RS485_Check_UART_Data(&mstp_port); + INCREMENT_AND_LIMIT_UINT8(EventCount); + MSTP_Receive_Frame_FSM(&mstp_port); + zassert_true(mstp_port.DataAvailable == false, NULL); + zassert_true(mstp_port.SilenceTimer(&mstp_port) == 0, NULL); + zassert_true(mstp_port.EventCount == EventCount, NULL); + } + zassert_true(mstp_port.ReceivedInvalidFrame == true, NULL); + zassert_true(mstp_port.ReceivedValidFrame == false, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + /* Proprietary Data */ + mstp_port.ReceivedInvalidFrame = false; + mstp_port.ReceivedValidFrame = false; + memset(data_proprietary, 0, sizeof(data_proprietary)); + len = MSTP_Create_Frame(buffer, sizeof(buffer), FRAME_TYPE_PROPRIETARY_MIN, + my_mac, my_mac, data_proprietary, sizeof(data_proprietary)); + zassert_true(len > 0, NULL); + Load_Input_Buffer(buffer, len); + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + while (mstp_port.receive_state != MSTP_RECEIVE_STATE_IDLE) { + RS485_Check_UART_Data(&mstp_port); + MSTP_Receive_Frame_FSM(&mstp_port); + } + zassert_true(mstp_port.DataLength == sizeof(data_proprietary), NULL); + zassert_true(mstp_port.ReceivedInvalidFrame == false, NULL); + zassert_true(mstp_port.ReceivedValidFrame == true, NULL); + zassert_true(mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE, NULL); + return; +} + +static void testMasterNodeFSM(void) +{ + struct mstp_port_struct_t MSTP_Port; /* port data */ + uint8_t my_mac = 0x05; /* local MAC address */ + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + + MSTP_Port.Nmax_info_frames = 1; + MSTP_Port.Nmax_master = 127; + + 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.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + + MSTP_Port.This_Station = my_mac; + + MSTP_Init(&MSTP_Port); + zassert_true(MSTP_Port.master_state == MSTP_MASTER_STATE_INITIALIZE, NULL); + /* FIXME: write a unit test for the Master Node State Machine */ +} + +static void testSlaveNodeFSM(void) +{ + struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */ + bool transition_now, non_zero; + unsigned slots, silence, i; + + MSTP_Port.InputBuffer = &RxBuffer[0]; + MSTP_Port.InputBufferSize = sizeof(RxBuffer); + MSTP_Port.OutputBuffer = &TxBuffer[0]; + MSTP_Port.OutputBufferSize = sizeof(TxBuffer); + + MSTP_Port.Nmax_info_frames = 0; + MSTP_Port.Nmax_master = 0; + + 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.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; + + MSTP_Port.This_Station = 128; + + MSTP_Init(&MSTP_Port); + MSTP_Slave_Node_FSM(&MSTP_Port); + zassert_true(MSTP_Port.master_state == MSTP_MASTER_STATE_IDLE, NULL); +} + +static void testZeroConfigNode_Init(struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + mstp_port->InputBuffer = &RxBuffer[0]; + mstp_port->InputBufferSize = sizeof(RxBuffer); + mstp_port->OutputBuffer = &TxBuffer[0]; + mstp_port->OutputBufferSize = sizeof(TxBuffer); + + mstp_port->Nmax_info_frames = 1; + mstp_port->Nmax_master = 127; + + 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->SilenceTimer = Timer_Silence; + mstp_port->SilenceTimerReset = Timer_Silence_Reset; + + /* configure for Zero Config */ + mstp_port->ZeroConfigEnabled = true; + mstp_port->This_Station = 255; + + MSTP_Init(mstp_port); + zassert_true(mstp_port->master_state == MSTP_MASTER_STATE_INITIALIZE, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_INIT, NULL); + zassert_true(mstp_port->Tframe_abort == DEFAULT_Tframe_abort, NULL); + zassert_true(mstp_port->Treply_delay == DEFAULT_Treply_delay, NULL); + zassert_true(mstp_port->Treply_timeout == DEFAULT_Treply_timeout, NULL); + zassert_true(mstp_port->Tusage_timeout == DEFAULT_Tusage_timeout, NULL); + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_IDLE, NULL); + zassert_true(mstp_port->Poll_Count == 0, NULL); + zassert_true(mstp_port->Zero_Config_Station == 64, NULL); + zassert_true(mstp_port->Npoll_slot >= 1, NULL); + zassert_true(mstp_port->Npoll_slot <= 64, NULL); + slots = 128 + mstp_port->Npoll_slot; + silence = Tno_token + Tslot * slots; + zassert_true(mstp_port->Zero_Config_Silence == silence, NULL); + non_zero = false; + for (i = 0; i < MSTP_UUID_SIZE; i++) { + if (mstp_port->UUID[i] > 0) { + non_zero = true; + } + } + zassert_true(non_zero, NULL); + zassert_true(mstp_port->Zero_Config_Max_Master == 0, NULL); +} + +static void testZeroConfigNode_No_Events_Timeout( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + SilenceTime = mstp_port->Zero_Config_Silence + 1; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_CONFIRM, NULL); +} + +static void testZeroConfigNode_Test_Request_Unsupported( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + /* test case: remote node does not support Test-Request; timeout */ + SilenceTime = mstp_port->Treply_timeout + 1; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_true(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_USE, NULL); + zassert_true( + mstp_port->This_Station == mstp_port->Zero_Config_Station, NULL); +} + +static void testZeroConfigNode_Test_Request_Supported( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + /* test case: remote node supports Test-Request */ + SilenceTime = 0; + + mstp_port->DestinationAddress = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = 0; + mstp_port->FrameType = FRAME_TYPE_TEST_RESPONSE; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_true(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_USE, NULL); + zassert_true( + mstp_port->This_Station == mstp_port->Zero_Config_Station, NULL); +} + +static void testZeroConfigNode_Test_IDLE_InvalidFrame( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + /* test case: waiting for a invalid frame */ + SilenceTime = 0; + mstp_port->ReceivedInvalidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_IDLE, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_IDLE, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); +} + +static void testZeroConfigNode_Test_IDLE_ValidFrameTimeout( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + /* test case: get a valid frame, followed by timeout */ + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->DestinationAddress = 1; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_true(mstp_port->ReceivedValidFrame == true, NULL); + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + SilenceTime = mstp_port->Zero_Config_Silence + 1; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_IDLE, NULL); +} + +static void testZeroConfigNode_Test_IDLE_ValidFrame( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + + /* test case: get a valid frame, followed by timeout */ + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->DestinationAddress = 1; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_true(mstp_port->ReceivedValidFrame == true, NULL); +} + +static void testZeroConfigNode_Test_LURK_AddressInUse( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + uint8_t src, dst; + + /* test case: src emits a token from each MAC in the zero-config range */ + SilenceTime = 0; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + for (src = Nmin_poll_station; src <= Nmax_poll_station; src++) { + mstp_port->ReceivedValidFrame = true; + mstp_port->SourceAddress = src; + dst = (src + 1) % (Nmax_master_station + 1); + mstp_port->DestinationAddress = dst; + zassert_true(mstp_port->Zero_Config_Station == src, NULL); + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true(mstp_port->Zero_Config_Station != src, "src=%u zc=%u", src, + mstp_port->Zero_Config_Station); + } +} + +static void testZeroConfigNode_Test_LURK_LearnMaxMaster( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + uint8_t src, dst; + + /* test case: src emits a token from each MAC in the zero-config range */ + SilenceTime = 0; + mstp_port->SourceAddress = 0; + mstp_port->FrameType = FRAME_TYPE_POLL_FOR_MASTER; + for (dst = 1; dst <= Nmax_master_station; dst++) { + mstp_port->ReceivedValidFrame = true; + mstp_port->DestinationAddress = dst; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true(mstp_port->Zero_Config_Max_Master == dst, NULL); + } +} + +static void testZeroConfigNode_Test_LURK_Claim( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now, non_zero; + unsigned slots, silence, i; + uint8_t src = 0, dst, count, count_max, count_claim; + + /* test case: src emits a PFM from each MAC in the zero-config range */ + SilenceTime = 0; + mstp_port->SourceAddress = src; + mstp_port->FrameType = FRAME_TYPE_POLL_FOR_MASTER; + dst = Nmin_poll_station; + count_claim = Nmin_poll + mstp_port->Npoll_slot; + count_max = Nmin_poll + Nmax_poll_station; + for (count = 0; count < count_max; count++) { + /* simulate receiving PFM */ + mstp_port->ReceivedValidFrame = true; + mstp_port->DestinationAddress = dst; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_true((mstp_port->Zero_Config_Station) == dst, NULL); + if (mstp_port->Npoll_slot == count_claim) { + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_CLAIM, + NULL); + } else if (mstp_port->Zero_Config_State == + MSTP_ZERO_CONFIG_STATE_LURK) { + zassert_true( + mstp_port->Poll_Count == (count + 1), "count=%u", count); + zassert_true( + mstp_port->Zero_Config_State == MSTP_ZERO_CONFIG_STATE_LURK, + NULL); + } else { + break; + } + } + /* verify the Reply To Poll For Master was sent for confirmation */ + zassert_equal(mstp_port->OutputBuffer[2], FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, NULL); + zassert_equal(mstp_port->OutputBuffer[3], mstp_port->SourceAddress, NULL); + zassert_equal(mstp_port->OutputBuffer[4], mstp_port->Zero_Config_Station, NULL); +} + +static void testZeroConfigNode_Test_LURK_ClaimTokenForUs( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t src = 0, dst; + + /* ClaimTokenForUs */ + dst = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = src; + mstp_port->DestinationAddress = dst; + mstp_port->FrameType = FRAME_TYPE_TOKEN; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_CONFIRM, NULL); + /* verify the Test Request Frame was sent for confirmation */ + zassert_equal(mstp_port->OutputBuffer[2], FRAME_TYPE_TEST_REQUEST, NULL); + zassert_equal(mstp_port->OutputBuffer[3], mstp_port->SourceAddress, NULL); + zassert_equal(mstp_port->OutputBuffer[4], mstp_port->Zero_Config_Station, NULL); +} + +static void testZeroConfigNode_Test_LURK_ConfirmationSuccessful( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t src = 0, dst; + + /* ConfirmationSuccessful */ + dst = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = src; + mstp_port->DestinationAddress = dst; + mstp_port->FrameType = FRAME_TYPE_TEST_RESPONSE; + memcpy(mstp_port->InputBuffer, mstp_port->UUID, MSTP_UUID_SIZE); + mstp_port->DataLength = MSTP_UUID_SIZE; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_true(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(mstp_port->This_Station, mstp_port->Zero_Config_Station, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_USE, NULL); +} + +static void testZeroConfigNode_Test_LURK_ConfirmationAddressInUse( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t src, dst, test_station; + + /* ConfirmationAddressInUse */ + dst = mstp_port->Zero_Config_Station; + src = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = src; + mstp_port->DestinationAddress = dst; + mstp_port->FrameType = FRAME_TYPE_PROPRIETARY_MIN; + encode_unsigned16(&mstp_port->InputBuffer[0], BACNET_VENDOR_ID); + memcpy(&mstp_port->InputBuffer[2], mstp_port->UUID, MSTP_UUID_SIZE); + mstp_port->DataLength = MSTP_UUID_SIZE+2; + mstp_port->ReceivedValidFrame = true; + test_station = mstp_port->Zero_Config_Station + 1; + + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(test_station, mstp_port->Zero_Config_Station, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_LURK, NULL); +} + +static void testZeroConfigNode_Test_LURK_ConfirmationUnuccessful_UUID_Size( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t src = 0, dst; + + /* ConfirmationFailed */ + dst = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = src; + mstp_port->DestinationAddress = dst; + mstp_port->FrameType = FRAME_TYPE_TEST_RESPONSE; + memcpy(mstp_port->InputBuffer, mstp_port->UUID, MSTP_UUID_SIZE); + /* set to an invalid size */ + mstp_port->DataLength = MSTP_UUID_SIZE-1; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_IDLE, NULL); +} + +static void testZeroConfigNode_Test_LURK_ConfirmationUnuccessful_UUID( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t src = 0, dst; + + /* ConfirmationFailed */ + dst = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = src; + mstp_port->DestinationAddress = dst; + mstp_port->FrameType = FRAME_TYPE_TEST_RESPONSE; + memcpy(mstp_port->InputBuffer, mstp_port->UUID, MSTP_UUID_SIZE); + /* make the UUID invalid */ + mstp_port->InputBuffer[0] = ~mstp_port->InputBuffer[0]; + mstp_port->DataLength = MSTP_UUID_SIZE; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_IDLE, NULL); +} + +static void testZeroConfigNode_Test_LURK_ClaimAddressInUse( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + uint8_t station; + + /* ClaimAddressInUse */ + station = mstp_port->Zero_Config_Station; + mstp_port->SourceAddress = station; + mstp_port->DestinationAddress = 0; + mstp_port->FrameType = FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER; + mstp_port->ReceivedValidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedValidFrame == false, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_LURK, NULL); + zassert_equal(mstp_port->Zero_Config_Station, station + 1, NULL); +} + +static void testZeroConfigNode_Test_LURK_ClaimInvalidFrame( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + + /* ClaimInvalidFrame */ + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = true; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_true(mstp_port->ReceivedInvalidFrame == false, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_CLAIM, NULL); +} + +static void testZeroConfigNode_Test_LURK_ClaimLostToken( + struct mstp_port_struct_t *mstp_port) +{ + bool transition_now; + + /* ClaimLostToken */ + mstp_port->ReceivedValidFrame = false; + mstp_port->ReceivedInvalidFrame = false; + SilenceTime = mstp_port->Zero_Config_Silence + 1; + transition_now = MSTP_Master_Node_FSM(mstp_port); + zassert_false(transition_now, NULL); + zassert_equal(mstp_port->Zero_Config_State, MSTP_ZERO_CONFIG_STATE_IDLE, NULL); +} + + +static void testZeroConfigNodeFSM(void) +{ + struct mstp_port_struct_t MSTP_Port = { 0 }; /* port data */ + + /* test case: timeout event */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_No_Events_Timeout(&MSTP_Port); + testZeroConfigNode_Test_Request_Unsupported(&MSTP_Port); + /* test case: invalid frame event */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_InvalidFrame(&MSTP_Port); + /* test case: valid frame event and timeout */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrameTimeout(&MSTP_Port); + /* test case: valid frame event LURK Tokens: AddressInUse */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_AddressInUse(&MSTP_Port); + /* test case: valid frame event LURK PFMs: LearnMaxMaster */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_LearnMaxMaster(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimAddress + ConfirmationSuccessful */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimTokenForUs(&MSTP_Port); + testZeroConfigNode_Test_LURK_ConfirmationSuccessful(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimAddress + ConfirmationAddressInUse */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimTokenForUs(&MSTP_Port); + testZeroConfigNode_Test_LURK_ConfirmationAddressInUse(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimAddress + but Confirmation is Unsuccessful - UUID is invalid */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimTokenForUs(&MSTP_Port); + testZeroConfigNode_Test_LURK_ConfirmationUnuccessful_UUID(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimAddress + but Confirmation is Unsuccessful - UUID is too short */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimTokenForUs(&MSTP_Port); + testZeroConfigNode_Test_LURK_ConfirmationUnuccessful_UUID_Size(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimAddressInUse */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimAddressInUse(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimInvalidFrame */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimInvalidFrame(&MSTP_Port); + /* test case: valid frame event LURK PFMs: ClaimLostToken */ + testZeroConfigNode_Init(&MSTP_Port); + testZeroConfigNode_Test_IDLE_ValidFrame(&MSTP_Port); + testZeroConfigNode_Test_LURK_Claim(&MSTP_Port); + testZeroConfigNode_Test_LURK_ClaimLostToken(&MSTP_Port); +} + +/** + * @} + */ + +void test_main(void) +{ + ztest_test_suite(crc_tests, ztest_unit_test(testReceiveNodeFSM), + ztest_unit_test(testMasterNodeFSM), ztest_unit_test(testSlaveNodeFSM), + ztest_unit_test(testZeroConfigNodeFSM)); + + ztest_run_test_suite(crc_tests); +}