diff --git a/bacnet-stack/.svn/README.txt b/bacnet-stack/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/.svn/empty-file b/bacnet-stack/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/.svn/entries b/bacnet-stack/.svn/entries
new file mode 100644
index 00000000..7a4728e7
--- /dev/null
+++ b/bacnet-stack/.svn/entries
@@ -0,0 +1,157 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bacnet-stack/.svn/format b/bacnet-stack/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/.svn/prop-base/Makefile.svn-base b/bacnet-stack/.svn/prop-base/Makefile.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/Makefile.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/bytes.h.svn-base b/bacnet-stack/.svn/prop-base/bytes.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/bytes.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/crc.c.svn-base b/bacnet-stack/.svn/prop-base/crc.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/crc.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/crc.h.svn-base b/bacnet-stack/.svn/prop-base/crc.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/crc.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/crc.ide.svn-base b/bacnet-stack/.svn/prop-base/crc.ide.svn-base
new file mode 100644
index 00000000..5e9587e6
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/crc.ide.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/bacnet-stack/.svn/prop-base/main.c.svn-base b/bacnet-stack/.svn/prop-base/main.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/main.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/mstp.c.svn-base b/bacnet-stack/.svn/prop-base/mstp.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/mstp.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/mstp.h.svn-base b/bacnet-stack/.svn/prop-base/mstp.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/mstp.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/mstp.ide.svn-base b/bacnet-stack/.svn/prop-base/mstp.ide.svn-base
new file mode 100644
index 00000000..5e9587e6
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/mstp.ide.svn-base
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/bacnet-stack/.svn/prop-base/ringbuf.c.svn-base b/bacnet-stack/.svn/prop-base/ringbuf.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/ringbuf.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/ringbuf.h.svn-base b/bacnet-stack/.svn/prop-base/ringbuf.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/ringbuf.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/rs485.h.svn-base b/bacnet-stack/.svn/prop-base/rs485.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/rs485.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/stdbool.h.svn-base b/bacnet-stack/.svn/prop-base/stdbool.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/stdbool.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/stdint.h.svn-base b/bacnet-stack/.svn/prop-base/stdint.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/stdint.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/prop-base/test.sh.svn-base b/bacnet-stack/.svn/prop-base/test.sh.svn-base
new file mode 100644
index 00000000..869ac71c
--- /dev/null
+++ b/bacnet-stack/.svn/prop-base/test.sh.svn-base
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/bacnet-stack/.svn/props/Makefile.svn-work b/bacnet-stack/.svn/props/Makefile.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/Makefile.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/bytes.h.svn-work b/bacnet-stack/.svn/props/bytes.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/bytes.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/crc.c.svn-work b/bacnet-stack/.svn/props/crc.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/crc.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/crc.h.svn-work b/bacnet-stack/.svn/props/crc.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/crc.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/crc.ide.svn-work b/bacnet-stack/.svn/props/crc.ide.svn-work
new file mode 100644
index 00000000..5e9587e6
--- /dev/null
+++ b/bacnet-stack/.svn/props/crc.ide.svn-work
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/bacnet-stack/.svn/props/main.c.svn-work b/bacnet-stack/.svn/props/main.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/main.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/mstp.c.svn-work b/bacnet-stack/.svn/props/mstp.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/mstp.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/mstp.h.svn-work b/bacnet-stack/.svn/props/mstp.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/mstp.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/mstp.ide.svn-work b/bacnet-stack/.svn/props/mstp.ide.svn-work
new file mode 100644
index 00000000..5e9587e6
--- /dev/null
+++ b/bacnet-stack/.svn/props/mstp.ide.svn-work
@@ -0,0 +1,5 @@
+K 13
+svn:mime-type
+V 24
+application/octet-stream
+END
diff --git a/bacnet-stack/.svn/props/ringbuf.c.svn-work b/bacnet-stack/.svn/props/ringbuf.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/ringbuf.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/ringbuf.h.svn-work b/bacnet-stack/.svn/props/ringbuf.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/ringbuf.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/rs485.h.svn-work b/bacnet-stack/.svn/props/rs485.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/rs485.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/stdbool.h.svn-work b/bacnet-stack/.svn/props/stdbool.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/stdbool.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/stdint.h.svn-work b/bacnet-stack/.svn/props/stdint.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/.svn/props/stdint.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/.svn/props/test.sh.svn-work b/bacnet-stack/.svn/props/test.sh.svn-work
new file mode 100644
index 00000000..869ac71c
--- /dev/null
+++ b/bacnet-stack/.svn/props/test.sh.svn-work
@@ -0,0 +1,5 @@
+K 14
+svn:executable
+V 1
+*
+END
diff --git a/bacnet-stack/.svn/text-base/Makefile.svn-base b/bacnet-stack/.svn/text-base/Makefile.svn-base
new file mode 100644
index 00000000..a555ed31
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/Makefile.svn-base
@@ -0,0 +1,29 @@
+#Makefile to build BACnet Application
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -g
+
+OBJS = main.o mstp.o crc.o ringbuf.o ports/linux/rs485.o
+
+TARGET = bacnet
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/.svn/text-base/bytes.h.svn-base b/bacnet-stack/.svn/text-base/bytes.h.svn-base
new file mode 100644
index 00000000..97dce0bd
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/bytes.h.svn-base
@@ -0,0 +1,42 @@
+// Defines the bit/byte/word/long conversions that are used in code
+
+#ifndef BYTES_H
+#define BYTES_H
+
+#include
+
+#ifndef LO_NIB
+ #define LO_NIB(b) ((b) & 0xF)
+#endif
+
+#ifndef HI_NIB
+ #define HI_NIB(b) ((b) >> 4)
+#endif
+
+#ifndef LO_BYTE
+ #define LO_BYTE(w) ((uint8_t)(w))
+#endif
+
+#ifndef HI_BYTE
+ #define HI_BYTE(w) ((uint8_t)((uint16_t)(w) >> 8))
+#endif
+
+#ifndef LO_WORD
+ #define LO_WORD(x) ((uint16_t)(x))
+#endif
+
+#ifndef HI_WORD
+ #define HI_WORD(x) ((uint16_t)((uint32_t)(x) >> 16))
+#endif
+
+#ifndef MAKE_WORD
+ #define MAKE_WORD(lo,hi) \
+ ((uint16_t)(((uint8_t)(lo))|(((uint16_t)((uint8_t)(hi)))<<8)))
+#endif
+
+#ifndef MAKE_LONG
+ #define MAKE_LONG(lo,hi) \
+ ((uint32_t)(((uint16_t)(lo))|(((uint32_t)((uint16_t)(hi)))<<16)))
+#endif
+
+#endif // end of header file
diff --git a/bacnet-stack/.svn/text-base/crc.c.svn-base b/bacnet-stack/.svn/text-base/crc.c.svn-base
new file mode 100644
index 00000000..6c429155
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/crc.c.svn-base
@@ -0,0 +1,153 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+#include
+#include
+#include
+
+// Accumulate "dataValue" into the CRC in crcValue.
+// Return value is updated CRC
+//
+// The ^ operator means exclusive OR.
+// Note: This function is copied directly from the BACnet standard.
+uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue)
+{
+ uint16_t crc;
+
+ crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
+
+ /* Exclusive OR the terms in the table (top down) */
+ crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
+ ^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
+ ^ (crc << 7);
+
+ /* Combine bits shifted out left hand end */
+ return (crc & 0xfe) ^ ((crc >> 8) & 1);
+}
+
+// Accumulate "dataValue" into the CRC in crcValue.
+// Return value is updated CRC
+//
+// The ^ operator means exclusive OR.
+// Note: This function is copied directly from the BACnet standard.
+uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue)
+{
+ uint16_t crcLow;
+
+ crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
+
+ /* Exclusive OR the terms in the table (top down) */
+ return (crcValue >>8) ^ (crcLow << 8) ^ (crcLow <<3)
+ ^ (crcLow <<12) ^ (crcLow >> 4)
+ ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
+}
+
+#ifdef TEST
+#include
+#include
+#include "ctest.h"
+#include "bytes.h"
+
+// test from Annex G 1.0 of BACnet Standard
+void testCRC8(Test* pTest)
+{
+ uint8_t crc = 0xff; // accumulates the crc value
+ uint8_t frame_crc; // appended to the end of the frame
+
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x55);
+ crc = CRC_Calc_Header(0x10,crc);
+ ct_test(pTest,crc == 0xC2);
+ crc = CRC_Calc_Header(0x05,crc);
+ ct_test(pTest,crc == 0xBC);
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x95);
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x73);
+ // send the ones complement of the CRC in place of
+ // the CRC, and the resulting CRC will always equal 0x55.
+ frame_crc = ~crc;
+ ct_test(pTest,frame_crc == 0x8C);
+ // use the ones complement value and the next to last CRC value
+ crc = CRC_Calc_Header(frame_crc,crc);
+ ct_test(pTest,crc == 0x55);
+}
+
+// test from Annex G 2.0 of BACnet Standard
+void testCRC16(Test* pTest)
+{
+ uint16_t crc = 0xffff;
+ uint16_t data_crc;
+
+ crc = CRC_Calc_Data(0x01,crc);
+ ct_test(pTest,crc == 0x1E0E);
+ crc = CRC_Calc_Data(0x22,crc);
+ ct_test(pTest,crc == 0xEB70);
+ crc = CRC_Calc_Data(0x30,crc);
+ ct_test(pTest,crc == 0x42EF);
+ // send the ones complement of the CRC in place of
+ // the CRC, and the resulting CRC will always equal 0xF0B8.
+ data_crc = ~crc;
+ ct_test(pTest,data_crc == 0xBD10);
+ crc = CRC_Calc_Data(LO_BYTE(data_crc),crc);
+ ct_test(pTest,crc == 0x0F3A);
+ crc = CRC_Calc_Data(HI_BYTE(data_crc),crc);
+ ct_test(pTest,crc == 0xF0B8);
+}
+
+#endif
+
+#ifdef TEST_CRC
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("crc", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testCRC8);
+ assert(rc);
+ rc = ct_addTestFunction(pTest, testCRC16);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
diff --git a/bacnet-stack/.svn/text-base/crc.h.svn-base b/bacnet-stack/.svn/text-base/crc.h.svn-base
new file mode 100644
index 00000000..afc0d6d4
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/crc.h.svn-base
@@ -0,0 +1,44 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+#ifndef CRC_H
+#define CRC_H
+
+#include
+#include
+
+uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue);
+uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue);
+
+#endif
diff --git a/bacnet-stack/.svn/text-base/crc.ide.svn-base b/bacnet-stack/.svn/text-base/crc.ide.svn-base
new file mode 100644
index 00000000..d7b2030c
Binary files /dev/null and b/bacnet-stack/.svn/text-base/crc.ide.svn-base differ
diff --git a/bacnet-stack/.svn/text-base/crc.mak.svn-base b/bacnet-stack/.svn/text-base/crc.mak.svn-base
new file mode 100644
index 00000000..e5bb862f
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/crc.mak.svn-base
@@ -0,0 +1,29 @@
+#Makefile to build CRC tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_CRC -g
+
+OBJS = crc.o test/ctest.o
+
+TARGET = crc
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/.svn/text-base/main.c.svn-base b/bacnet-stack/.svn/text-base/main.c.svn-base
new file mode 100644
index 00000000..c54fdc5b
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/main.c.svn-base
@@ -0,0 +1,63 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#include
+#include
+#include "mstp.h"
+#include "bytes.h"
+#include "crc.h"
+#include "rs485.h"
+#include "ringbuf.h"
+
+void main(void)
+{
+ struct mstp_port_struct_t mstp_port; // port data
+ uint8_t my_mac = 0x05; // local MAC address
+
+ MSTP_Init(&mstp_port,my_mac);
+
+ // loop forever
+ for (;;)
+ {
+ // input
+ RS485_Check_UART_Data(&mstp_port);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // process
+
+ // output
+ MSTP_Master_Node_FSM(&mstp_port);
+
+ }
+}
diff --git a/bacnet-stack/.svn/text-base/mstp.c.svn-base b/bacnet-stack/.svn/text-base/mstp.c.svn-base
new file mode 100644
index 00000000..a1214073
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/mstp.c.svn-base
@@ -0,0 +1,1671 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2003 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// This clause describes a Master-Slave/Token-Passing (MS/TP) data link
+// protocol, which provides the same services to the network layer as
+// ISO 8802-2 Logical Link Control. It uses services provided by the
+// EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be
+// included in this standard by reference. The following hardware is assumed:
+// (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of
+// transmitting and receiving eight data bits with one stop bit
+// and no parity.
+// (b) An EIA-485 transceiver whose driver may be disabled.
+// (c) A timer with a resolution of five milliseconds or less
+
+#include
+#include
+#include "mstp.h"
+#include "bytes.h"
+#include "crc.h"
+#include "rs485.h"
+#include "ringbuf.h"
+
+// MS/TP Frame Format
+// All frames are of the following format:
+//
+// Preamble: two octet preamble: X`55', X`FF'
+// Frame Type: one octet
+// Destination Address: one octet address
+// Source Address: one octet address
+// Length: two octets, most significant octet first, of the Data field
+// Header CRC: one octet
+// Data: (present only if Length is non-zero)
+// Data CRC: (present only if Length is non-zero) two octets,
+// least significant octet first
+// (pad): (optional) at most one octet of padding: X'FF'
+
+ // The number of tokens received or used before a Poll For Master cycle
+// is executed: 50.
+const unsigned Npoll = 50;
+
+// The number of retries on sending Token: 1.
+const unsigned Nretry_token = 1;
+
+// The minimum number of DataAvailable or ReceiveError events that must be
+// seen by a receiving node in order to declare the line "active": 4.
+const unsigned 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 unsigned Tframe_abort = 1 + ((1000 * 60) / 9600);
+
+// The maximum idle time a sending node may allow to elapse between octets
+// of a frame the node is transmitting: 20 bit times.
+const unsigned Tframe_gap = 20;
+
+// The time without a DataAvailable or ReceiveError event before declaration
+// of loss of token: 500 milliseconds.
+const unsigned Tno_token = 500;
+
+// The maximum time after the end of the stop bit of the final
+// octet of a transmitted frame before a node must disable its
+// EIA-485 driver: 15 bit times.
+const unsigned Tpostdrive = 15;
+
+// 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.
+const unsigned Treply_delay = 225;
+
+// 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.)
+const unsigned Treply_timeout = 255;
+
+// 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.
+const unsigned Troff = 30;
+
+// The width of the time slot within which a node may generate a token:
+// 10 milliseconds.
+const unsigned Tslot = 10;
+
+// The maximum time a node may wait after reception of the token or
+// a Poll For Master frame before sending the first octet of a frame:
+// 15 milliseconds.
+const unsigned Tusage_delay = 15;
+
+// 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.)
+const unsigned Tusage_timeout = 20;
+
+// Millisecond Timer - called every millisecond
+void MSTP_Millisecond_Timer(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->SilenceTimer < 255)
+ mstp_port->SilenceTimer++;
+ if (mstp_port->ReplyPostponedTimer < 255)
+ mstp_port->ReplyPostponedTimer++;
+
+ return;
+}
+
+void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *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:
+ // EatAnError
+ if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if (mstp_port->DataAvailable == TRUE)
+ {
+ // Preamble1
+ if (mstp_port->DataRegister == 0x55)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // receive the remainder of the frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
+ }
+ // EatAnOctet
+ else
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ }
+ break;
+ // In the PREAMBLE state, the node waits for the second octet of the preamble.
+ case MSTP_RECEIVE_STATE_PREAMBLE:
+ // Timeout
+ if (mstp_port->SilenceTimer > 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)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if (mstp_port->DataAvailable == TRUE)
+ {
+ // Preamble2
+ if (mstp_port->DataRegister == 0xFF)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ 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)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the second preamble octet.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
+ }
+ // NotPreamble
+ else
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ }
+ break;
+ // In the HEADER state, the node waits for the fixed message header.
+ case MSTP_RECEIVE_STATE_HEADER:
+ // Timeout
+ if (mstp_port->SilenceTimer > Tframe_abort)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+
+ // Error
+ else if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else if (mstp_port->DataAvailable == TRUE)
+ {
+ // FrameType
+ if (mstp_port->Index == 0)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->FrameType = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 1;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Destination
+ else if (mstp_port->Index == 1)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DestinationAddress = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 2;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Source
+ else if (mstp_port->Index == 2)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->SourceAddress = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 3;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Length1
+ else if (mstp_port->Index == 3)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataLength = mstp_port->DataRegister * 256;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 4;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Length2
+ else if (mstp_port->Index == 4)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataLength += mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 5;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // HeaderCRC
+ else if (mstp_port->Index == 5)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER_CRC;
+ }
+ // not per MS/TP standard, but it is a case not covered
+ else
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // indicate that an error has occurred during
+ // the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ break;
+ // In the HEADER_CRC state, the node validates the CRC on the fixed
+ // message header.
+ case MSTP_RECEIVE_STATE_HEADER_CRC:
+ // BadCRC
+ if (mstp_port->HeaderCRC != 0x55)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if ((mstp_port->DestinationAddress == mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS))
+ {
+ // FrameTooLong
+ if (mstp_port->DataLength > INPUT_BUFFER_SIZE)
+ {
+ // indicate that a frame with an illegal or
+ // unacceptable data length has been received
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // NoData
+ else if (mstp_port->DataLength == 0)
+ {
+ // 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;
+ }
+ // Data
+ else
+ {
+ mstp_port->Index = 0;
+ mstp_port->DataCRC = 0xFFFF;
+ // receive the data portion of the frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ }
+ // NotForUs
+ else
+ {
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ break;
+ // In the DATA state, the node waits for the data portion of a frame.
+ case MSTP_RECEIVE_STATE_DATA:
+ // Timeout
+ if (mstp_port->SilenceTimer > Tframe_abort)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // Error
+ else if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else if (mstp_port->DataAvailable == TRUE)
+ {
+ // DataOctet
+ if (mstp_port->Index < mstp_port->DataLength)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->InputBuffer[mstp_port->Index] = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index++;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ // CRC1
+ else if (mstp_port->Index == mstp_port->DataLength)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index++; // Index now becomes the number of data octets
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ // CRC2
+ else if (mstp_port->Index == (mstp_port->DataLength + 1))
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA_CRC;
+ }
+ }
+ break;
+ // In the DATA_CRC state, the node validates the CRC of the message data.
+ case MSTP_RECEIVE_STATE_DATA_CRC:
+ // GoodCRC
+ if (mstp_port->DataCRC == 0xF0B8)
+ {
+ // indicate the complete reception of a valid frame
+ mstp_port->ReceivedValidFrame = TRUE;
+
+ // now might be a good time to process the message or
+ // copy the data to a buffer so that we can process the message
+
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // BadCRC
+ else
+ {
+ // to indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ break;
+ default:
+ // shouldn't get here - but if we do...
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ break;
+ }
+
+ return;
+}
+
+void MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
+{
+
+ switch (mstp_port->master_state)
+ {
+ case MSTP_MASTER_STATE_INITIALIZE:
+ // DoneInitializing
+ mstp_port->This_Station = 0; // FIXME: the node's station address
+ // 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->ReceivedValidFrame = FALSE;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ break;
+ // In the IDLE state, the node waits for a frame.
+ case MSTP_MASTER_STATE_IDLE:
+ // LostToken
+ if (mstp_port->SilenceTimer >= Tno_token)
+ {
+ // assume that the token has been lost
+ mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN;
+ }
+ // mstp_port->ReceivedInvalidFrame
+ else if (mstp_port->ReceivedInvalidFrame == TRUE)
+ {
+ // invalid frame was received
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedUnwantedFrame
+ else if (mstp_port->ReceivedValidFrame == TRUE)
+ {
+ if ((mstp_port->DestinationAddress != mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS))
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // DestinationAddress is equal to 255 (broadcast) and
+ // FrameType has a value of Token, BACnet Data Expecting Reply, Test_Request,
+ // or a proprietary type known to this node that expects a reply
+ // (such frames may not be broadcast), or
+ else if ((mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TOKEN) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)))
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // FrameType has a value that indicates a standard or proprietary type
+ // that is not known to this node.
+ // FIXME: change this if you add a proprietary type
+ else if /*(*/(mstp_port->FrameType >= FRAME_TYPE_PROPRIETARY_MIN) /*&&*/
+ /*(FrameType <= FRAME_TYPE_PROPRIETARY_MAX))*/
+ /* unnecessary if FrameType is uint8_t with max of 255 */
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedToken
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_TOKEN))
+ {
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->FrameCount = 0;
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // ReceivedPFM
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_POLL_FOR_MASTER))
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedDataNoReply
+ // or a proprietary type known to this node that does not expect a reply
+ else if (((mstp_port->DestinationAddress == mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) &&
+ ((mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY) ||
+ // (mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE)))
+ {
+ // FIXME: indicate successful reception to the higher layers
+ // i.e. Process this frame!
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedDataNeedingReply
+ // or a proprietary type known to this node that expects a reply
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ ((mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) ||
+ // (mstp_port->FrameType == FRAME_TYPE_PROPRIETARY) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)))
+ {
+ mstp_port->ReplyPostponedTimer = 0;
+ // indicate successful reception to the higher layers
+ // (management entity in the case of Test_Request);
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_ANSWER_DATA_REQUEST;
+ }
+ }
+ break;
+ // In the USE_TOKEN state, the node is allowed to send one or
+ // more data frames. These may be BACnet Data frames or
+ // proprietary frames.
+ case MSTP_MASTER_STATE_USE_TOKEN:
+ // NothingToSend
+ // FIXME: If there is no data frame awaiting transmission,
+ {
+ mstp_port->FrameCount = mstp_port->Nmax_info_frames;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // SendNoWait
+ // FIXME: If there is a frame awaiting transmission that
+ // is of type Test_Response, BACnet Data Not Expecting Reply,
+ // or a proprietary type that does not expect a reply,
+// {
+// // transmit the data frame
+// RS485_Send_Frame(?????????????);
+// FrameCount++;
+// mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+// }
+ // SendAndWait
+ // FIXME: If there is a frame awaiting transmission that is of
+ // type Test_Request, BACnet Data Expecting Reply, or
+ // a proprietary type that expects a reply,
+// {
+// // transmit the data frame
+// RS485_Send_Frame();
+// FrameCount++;
+// mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY;
+// }
+ // In the WAIT_FOR_REPLY state, the node waits for
+ // a reply from another node.
+ case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
+ // ReplyTimeout
+ if (mstp_port->SilenceTimer >= Treply_timeout)
+ {
+ // assume that the request has failed
+ mstp_port->FrameCount = mstp_port->Nmax_info_frames;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ // Any retry of the data frame shall await the next entry
+ // to the USE_TOKEN state. (Because of the length of the timeout,
+ // this transition will cause the token to be passed regardless
+ // of the initial value of FrameCount.)
+ }
+ // InvalidFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedInvalidFrame == TRUE))
+ {
+ // error in frame reception
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedReply
+ // or a proprietary type that indicates a reply
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE) ||
+ //(mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY)))
+ {
+ // FIXME: indicate successful reception to the higher layers
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedPostpone
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_REPLY_POSTPONED))
+ {
+ // FIXME: then the reply to the message has been postponed until a later time.
+ // So, what does this really mean?
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress != mstp_port->This_Station))
+ //the expected reply should not be broadcast)
+ {
+ // an unexpected frame was received
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // Synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE) ||
+ //(mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY)))
+ {
+ // An unexpected frame was received.
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // Synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ break;
+ // The DONE_WITH_TOKEN state either sends another data frame,
+ // passes the token, or initiates a Poll For Master cycle.
+ case MSTP_MASTER_STATE_DONE_WITH_TOKEN:
+ // SendAnotherFrame
+ if (mstp_port->FrameCount < mstp_port->Nmax_info_frames)
+ {
+ // then this node may send another information frame
+ // before passing the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // mstp_port->SoleMaster
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount < Npoll) &&
+ (mstp_port->SoleMaster == TRUE))
+ {
+ // there are no other known master nodes to
+ // which the token may be sent (true master-slave operation).
+ mstp_port->FrameCount = 0;
+ mstp_port->TokenCount++;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // SendToken
+ else if (((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount < Npoll) &&
+ (mstp_port->SoleMaster == FALSE)) ||
+ // The comparison of NS and TS+1 eliminates the Poll For Master
+ // if there are no addresses between TS and NS, since there is no
+ // address at which a new master node may be found in that case.
+ (mstp_port->Next_Station ==
+ (uint8_t)((mstp_port->This_Station +1) % (mstp_port->Nmax_master + 1))))
+ {
+ mstp_port->TokenCount++;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SendMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) != mstp_port->Next_Station))
+ {
+ mstp_port->Poll_Station = (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1);
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ // ResetMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) == mstp_port->Next_Station) &&
+ (mstp_port->SoleMaster == FALSE))
+ {
+ mstp_port->Poll_Station = mstp_port->This_Station;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SoleMasterRestartMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) == mstp_port->Next_Station) &&
+ (mstp_port->SoleMaster == TRUE))
+ {
+ mstp_port->Poll_Station = (mstp_port->Next_Station +1) % (mstp_port->Nmax_master + 1);
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // no known successor node
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // find a new successor to TS
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ // The PASS_TOKEN state listens for a successor to begin using
+ // the token that this node has just attempted to pass.
+ case MSTP_MASTER_STATE_PASS_TOKEN:
+ // SawTokenUser
+ if ((mstp_port->SilenceTimer < Tusage_timeout) &&
+ (mstp_port->EventCount > Nmin_octets))
+ {
+ // Assume that a frame has been sent by the new token user.
+ // Enter the IDLE state to process the frame.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // RetrySendToken
+ else if ((mstp_port->SilenceTimer >= Tusage_timeout) &&
+ (mstp_port->RetryCount < Nretry_token))
+ {
+ mstp_port->RetryCount++;
+ // Transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->EventCount = 0;
+ // re-enter the current state to listen for NS
+ // to begin using the token.
+ }
+ // FindNewSuccessor
+ else if ((mstp_port->SilenceTimer >= Tusage_timeout) &&
+ (mstp_port->RetryCount >= Nretry_token))
+ {
+ // Assume that NS has failed.
+ mstp_port->Poll_Station = (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // no known successor node
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // find a new successor to TS
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ break;
+ // The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater
+ // than Tno_token, indicating that there has been no network activity
+ // for that period of time. The timeout is continued to determine
+ // whether or not this node may create a token.
+ case MSTP_MASTER_STATE_NO_TOKEN:
+ // SawFrame
+ if ((mstp_port->SilenceTimer < (Tno_token + (Tslot * mstp_port->This_Station))) &&
+ (mstp_port->EventCount > Nmin_octets))
+ {
+ // Some other node exists at a lower address.
+ // Enter the IDLE state to receive and process the incoming frame.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // GenerateToken
+ else if ((mstp_port->SilenceTimer >= (Tno_token + (Tslot * mstp_port->This_Station))) &&
+ (mstp_port->SilenceTimer < (Tno_token + (Tslot * (mstp_port->This_Station + 1)))))
+ {
+ // Assume that this node is the lowest numerical address
+ // on the network and is empowered to create a token.
+ mstp_port->Poll_Station = (mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // indicate that the next station is unknown
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // enter the POLL_FOR_MASTER state to find a new successor to TS.
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ break;
+ // In the POLL_FOR_MASTER state, the node listens for a reply to
+ // a previously sent Poll For Master frame in order to find
+ // a successor node.
+ case MSTP_MASTER_STATE_POLL_FOR_MASTER:
+ // ReceivedReplyToPFM
+ if ((mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER))
+ {
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->Next_Station = mstp_port->SourceAddress;
+ mstp_port->EventCount = 0;
+ // Transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->Poll_Station = mstp_port->This_Station;
+ mstp_port->TokenCount = 0;
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->ReceivedValidFrame == TRUE) &&
+ ((mstp_port->DestinationAddress != mstp_port->This_Station) ||
+ (mstp_port->FrameType != FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)))
+ {
+ // An unexpected frame was received.
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // enter the IDLE state to synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // mstp_port->SoleMaster
+ else if ((mstp_port->SoleMaster == TRUE) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // There was no valid reply to the periodic poll
+ // by the sole known master for other masters.
+ mstp_port->FrameCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // DoneWithPFM
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station != mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // There was no valid reply to the maintenance
+ // poll for a master at address PS.
+ mstp_port->EventCount = 0;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SendNextPFM
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station == mstp_port->This_Station) && // no known successor node
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) != mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ mstp_port->Poll_Station =
+ (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ // Re-enter the current state.
+ }
+ // DeclareSoleMaster
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station == mstp_port->This_Station) && // no known successor node
+ ((uint8_t)((mstp_port->Poll_Station + 1) %
+ (mstp_port->Nmax_master + 1)) == mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // to indicate that this station is the only master
+ mstp_port->SoleMaster = TRUE;
+ mstp_port->FrameCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ break;
+ // 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.
+ case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST:
+ if (mstp_port->ReplyPostponedTimer <= Treply_delay)
+ {
+ // 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 RS485_Send_Frame to transmit the reply frame
+ // and enter the IDLE state to wait for the next frame.
+
+ // Test Request
+ // If a receiving node can successfully receive and return
+ // the information field, it shall do so. If it cannot receive
+ // and return the entire information field but can detect
+ // the reception of a valid Test_Request frame
+ // (for example, by computing the CRC on octets as
+ // they are received), then the receiving node shall discard
+ // the information field and return a Test_Response containing
+ // no information field. If the receiving node cannot detect
+ // the valid reception of frames with overlength information fields,
+ // then no response shall be returned.
+ if (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TEST_RESPONSE,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ mstp_port->InputBuffer,
+ mstp_port->Index);
+ }
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+
+ //
+ // DeferredReply
+ // If no reply will be available from the higher layers
+ // within Treply_delay after the reception of the
+ // final octet of the requesting frame (the mechanism
+ // used to determine this is a local matter),
+ // then an immediate reply is not possible.
+ // Any reply shall wait until this node receives the token.
+ // Call RS485_Send_Frame to transmit a Reply Postponed frame,
+ // and enter the IDLE state.
+
+ else
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_REPLY_POSTPONED,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ break;
+ default:
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ break;
+ }
+
+ return;
+}
+
+void MSTP_Init(
+ struct mstp_port_struct_t *mstp_port,
+ uint8_t this_station_mac)
+{
+ int i; //loop counter
+
+ if (mstp_port)
+ {
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->DataRegister = 0;
+ mstp_port->DataCRC = 0;
+ mstp_port->DataCRC = 0;
+ mstp_port->DataLength = 0;
+ mstp_port->DestinationAddress = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->FrameType = FRAME_TYPE_TOKEN;
+ mstp_port->FrameCount = 0;
+ mstp_port->HeaderCRC = 0;
+ mstp_port->Index = 0;
+ mstp_port->Index = 0;
+ for (i = 0; i < sizeof(mstp_port->InputBuffer); i++)
+ {
+ mstp_port->InputBuffer[i] = 0;
+ }
+ mstp_port->Next_Station = this_station_mac;
+ mstp_port->Poll_Station = this_station_mac;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->RetryCount = 0;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->ReplyPostponedTimer = 0;
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->SourceAddress = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->This_Station = this_station_mac;
+ mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES;
+ mstp_port->Nmax_master = DEFAULT_MAX_MASTER;
+ }
+}
+
+unsigned MSTP_Create_Frame(
+ uint8_t *buffer, // where frame is loaded
+ unsigned 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t crc8 = 0xFF; // used to calculate the crc value
+ uint16_t crc16 = 0xFFFF; // used to calculate the crc value
+ unsigned index = 0; // used to load the data portion of the frame
+
+ // not enough to do a header
+ if (buffer_len < 8)
+ return 0;
+
+ 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 / 256;
+ crc8 = CRC_Calc_Header(buffer[5],crc8);
+ buffer[6] = data_len % 256;
+ crc8 = CRC_Calc_Header(buffer[6],crc8);
+ buffer[7] = ~crc8;
+
+ index = 8;
+ while (data_len && data && (index < buffer_len))
+ {
+ buffer[index] = *data;
+ crc16 = CRC_Calc_Data(buffer[index],crc16);
+ data++;
+ index++;
+ data_len--;
+ }
+ // append the data CRC if necessary
+ if (index > 8)
+ {
+ if ((index + 2) <= buffer_len)
+ {
+ crc16 = ~crc16;
+ buffer[index] = LO_BYTE(crc16);
+ index++;
+ buffer[index] = HI_BYTE(crc16);
+ index++;
+ }
+ else
+ return 0;
+ }
+
+ return index; // returns the frame length
+}
+
+
+#ifdef TEST
+#include
+#include
+#include "ctest.h"
+
+// test stub functions
+void RS485_Send_Frame(
+ struct mstp_port_struct_t *mstp_port, // port specific data
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ (void)mstp_port;
+ (void)frame_type;
+ (void)destination;
+ (void)source;
+ (void)data;
+ (void)data_len;
+}
+
+#define RING_BUFFER_DATA_SIZE 1
+#define RING_BUFFER_SIZE INPUT_BUFFER_SIZE
+static RING_BUFFER Test_Buffer;
+static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE];
+static void Load_Input_Buffer(uint8_t *buffer,size_t len)
+{
+ static bool initialized = FALSE; // tracks our init
+
+
+ if (!initialized)
+ {
+ initialized = TRUE;
+ Ringbuf_Init(
+ &Test_Buffer,
+ (char *)Test_Buffer_Data,
+ RING_BUFFER_DATA_SIZE,
+ RING_BUFFER_SIZE);
+ }
+
+ // empty any the existing data
+ while (!Ringbuf_Empty(&Test_Buffer))
+ {
+ (void)Ringbuf_Pop_Front(&Test_Buffer);
+ }
+
+ if (buffer)
+ {
+ while (len)
+ {
+ (void)Ringbuf_Put(&Test_Buffer,(char *)buffer);
+ len--;
+ buffer++;
+ }
+ }
+}
+
+void RS485_Check_UART_Data(
+ struct mstp_port_struct_t *mstp_port) // port specific data
+{
+ char *data;
+ if (!Ringbuf_Empty(&Test_Buffer) && mstp_port &&
+ (mstp_port->DataAvailable == FALSE))
+ {
+ data = Ringbuf_Pop_Front(&Test_Buffer);
+ if (data)
+ {
+ mstp_port->DataRegister = *data;
+ mstp_port->DataAvailable = TRUE;
+ }
+ }
+}
+
+void testReceiveNodeFSM(Test* pTest)
+{
+ 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[INPUT_BUFFER_SIZE] = {0};
+ uint8_t data[INPUT_BUFFER_SIZE - 8 /*header*/ - 2 /*CRC*/] = {0};
+
+ MSTP_Init(&mstp_port,my_mac);
+
+ // check the receive error during idle
+ mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE;
+ mstp_port.ReceiveError = TRUE;
+ mstp_port.SilenceTimer = 255;
+ mstp_port.EventCount = 0;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for bad packet header
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x11;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header, but timeout
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // force the timeout
+ mstp_port.SilenceTimer = Tframe_abort + 1;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble, but receive error
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // force the error
+ mstp_port.ReceiveError = TRUE;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble1, but bad preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // no change of state if no data yet
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // repeated preamble1
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // repeated preamble1
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // bad data
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x11;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble, but timeout in packet
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // force the timeout
+ mstp_port.SilenceTimer = Tframe_abort + 1;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+
+ // check for good packet header preamble, but error in packet
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // force the error
+ mstp_port.ReceiveError = TRUE;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // no change of state if no data yet
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 1);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,FrameType == FRAME_TYPE_TOKEN);
+ // Destination
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x10;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 2);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DestinationAddress == 0x10);
+ // Source
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = my_mac;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 3);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.SourceAddress == my_mac);
+ // Length1 = length*256
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 4);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DataLength == 0);
+ // Length2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 5);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DataLength == 0);
+ // HeaderCRC
+ mstp_port.DataAvailable = TRUE;
+ ct_test(pTest,HeaderCRC == 0x73); // per Annex G example
+ mstp_port.DataRegister = ~HeaderCRC; // one's compliment of CRC is sent
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 5);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ ct_test(pTest,mstp_port.HeaderCRC == 0x55);
+ // NotForUs
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ Load_Input_Buffer(buffer,len);
+ for (i = 0; i < len; i++)
+ {
+ RS485_Check_UART_Data(&mstp_port);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == FALSE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == TRUE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // Data
+ mstp_port.ReceivedInvalidFrame = FALSE;
+ mstp_port.ReceivedValidFrame = FALSE;
+ memset(data,0,sizeof(data));
+ len = MSTP_Create_Frame(
+ buffer,
+ sizeof(buffer),
+ FRAME_TYPE_PROPRIETARY_MIN,
+ my_mac, // destination
+ my_mac, // source
+ data, // data
+ sizeof(data)); // data size
+ ct_test(pTest,len > 0);
+ 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);
+ }
+ ct_test(pTest,mstp_port.DataLength == sizeof(data));
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == FALSE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == TRUE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ return;
+}
+
+void testMasterNodeFSM(Test* pTest)
+{
+ struct mstp_port_struct_t mstp_port; // port data
+ uint8_t my_mac = 0x05; // local MAC address
+
+ MSTP_Init(&mstp_port,my_mac);
+ ct_test(pTest,mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE);
+
+}
+
+#endif
+
+#ifdef TEST_MSTP
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("mstp", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testReceiveNodeFSM);
+ assert(rc);
+ rc = ct_addTestFunction(pTest, testMasterNodeFSM);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
diff --git a/bacnet-stack/.svn/text-base/mstp.h.svn-base b/bacnet-stack/.svn/text-base/mstp.h.svn-base
new file mode 100644
index 00000000..6ec810c3
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/mstp.h.svn-base
@@ -0,0 +1,240 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#ifndef MSTP_H
+#define MSTP_H
+
+#include
+#include
+#include
+
+// The number of elements in the array InputBuffer[].
+#define INPUT_BUFFER_SIZE (501)
+
+// The value 255 is used to denote broadcast when used as a
+// destination address but is not allowed as a value for a station.
+#define MSTP_BROADCAST_ADDRESS 255
+
+// MS/TP Frame Type
+// Frame Types 8 through 127 are reserved by ASHRAE.
+#define FRAME_TYPE_TOKEN 0
+#define FRAME_TYPE_POLL_FOR_MASTER 1
+#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2
+#define FRAME_TYPE_TEST_REQUEST 3
+#define FRAME_TYPE_TEST_RESPONSE 4
+#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5
+#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6
+#define FRAME_TYPE_REPLY_POSTPONED 7
+// Frame Types 128 through 255: Proprietary Frames
+// 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.
+#define FRAME_TYPE_PROPRIETARY_MIN 128
+#define FRAME_TYPE_PROPRIETARY_MAX 255
+
+// receive FSM states
+typedef enum
+{
+ MSTP_RECEIVE_STATE_IDLE,
+ MSTP_RECEIVE_STATE_PREAMBLE,
+ MSTP_RECEIVE_STATE_HEADER,
+ MSTP_RECEIVE_STATE_HEADER_CRC,
+ MSTP_RECEIVE_STATE_DATA,
+ MSTP_RECEIVE_STATE_DATA_CRC,
+} MSTP_RECEIVE_STATE;
+
+// master node FSM states
+typedef enum
+{
+ MSTP_MASTER_STATE_INITIALIZE,
+ MSTP_MASTER_STATE_IDLE,
+ MSTP_MASTER_STATE_USE_TOKEN,
+ MSTP_MASTER_STATE_WAIT_FOR_REPLY,
+ MSTP_MASTER_STATE_DONE_WITH_TOKEN,
+ MSTP_MASTER_STATE_PASS_TOKEN,
+ MSTP_MASTER_STATE_NO_TOKEN,
+ MSTP_MASTER_STATE_POLL_FOR_MASTER,
+ MSTP_MASTER_STATE_ANSWER_DATA_REQUEST,
+} MSTP_MASTER_STATE;
+
+// data for a given MS/TP port
+struct mstp_port_struct_t
+{
+ MSTP_RECEIVE_STATE receive_state;
+ // When a master node is powered up or reset,
+ // it shall unconditionally enter the INITIALIZE state.
+ MSTP_MASTER_STATE master_state;
+
+ bool ReceiveError; // TRUE when error detected during Rx octet
+
+ bool DataAvailable; // There is data in the buffer
+
+ uint8_t DataRegister; // stores the latest data
+
+ // Used to accumulate the CRC on the data field of a frame.
+ uint16_t DataCRC;
+
+ // Used to store the data length of a received frame.
+ unsigned 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.
+ unsigned 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
+ // pass the token.
+ unsigned FrameCount;
+
+ // Used to accumulate the CRC on the header of a frame.
+ uint8_t HeaderCRC;
+
+ // Used as an index by the Receive State Machine, up to a maximum value of
+ // InputBufferSize.
+ unsigned 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.
+ // A smaller value for InputBufferSize may be used by some implementations.
+ uint8_t InputBuffer[INPUT_BUFFER_SIZE];
+
+ // "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.
+ uint8_t Poll_Station;
+
+ // 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 main
+ // state machine.
+ bool ReceivedInvalidFrame;
+
+ // A Boolean flag set to TRUE by the Receive State Machine if a valid frame
+ // is received. Set to FALSE by the main state machine.
+ bool ReceivedValidFrame;
+
+ // 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
+ unsigned SilenceTimer;
+
+ // 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.
+ unsigned ReplyPostponedTimer;
+
+ // A Boolean flag set to TRUE by the master machine if this node is the
+ // only known master node.
+ bool SoleMaster;
+
+ // 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.
+ 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.
+ 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.
+ unsigned 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.
+ unsigned Nmax_master;
+
+ // After receiving a frame this value will be TRUE until Tturnaround
+ // has expired
+ bool Turn_Around_Waiting;
+};
+
+#define DEFAULT_MAX_INFO_FRAMES 1
+#define DEFAULT_MAX_MASTER 127
+
+// 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
+#define Tturnaround 40;
+
+void MSTP_Millisecond_Timer(struct mstp_port_struct_t *mstp_port);
+void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port);
+void MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port);
+
+unsigned MSTP_Create_Frame(
+ uint8_t *buffer, // where frame is loaded
+ unsigned 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
+ unsigned data_len); // number of bytes of data (up to 501)
+
+#endif
diff --git a/bacnet-stack/.svn/text-base/mstp.ide.svn-base b/bacnet-stack/.svn/text-base/mstp.ide.svn-base
new file mode 100644
index 00000000..986ae620
Binary files /dev/null and b/bacnet-stack/.svn/text-base/mstp.ide.svn-base differ
diff --git a/bacnet-stack/.svn/text-base/mstp.mak.svn-base b/bacnet-stack/.svn/text-base/mstp.mak.svn-base
new file mode 100644
index 00000000..d93124de
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/mstp.mak.svn-base
@@ -0,0 +1,29 @@
+#Makefile to build BACnet MS/TP tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_MSTP -g
+
+OBJS = mstp.o crc.o ringbuf.o test/ctest.o
+
+TARGET = mstp
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/.svn/text-base/ringbuf.c.svn-base b/bacnet-stack/.svn/text-base/ringbuf.c.svn-base
new file mode 100644
index 00000000..ea3745b4
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/ringbuf.c.svn-base
@@ -0,0 +1,287 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 by Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+/* Functional Description: Generic ring buffer library for deeply
+ embedded system. See the unit tests for usage examples. */
+
+#include "stdint.h"
+#include "ringbuf.h"
+
+/****************************************************************************
+* DESCRIPTION: Returns the empty/full status of the ring buffer
+* RETURN: TRUE if the ring buffer is empty, FALSE if it is not.
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+bool Ringbuf_Empty(RING_BUFFER const *b)
+{
+ return (b->count == 0);
+}
+
+/****************************************************************************
+* DESCRIPTION: Looks at the data from the head of the list without removing it
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+char *Ringbuf_Get_Front(RING_BUFFER const *b)
+{
+ return (b->count ? &(b->data[b->head * b->element_size]) : NULL);
+}
+
+/****************************************************************************
+* DESCRIPTION: Gets the data from the front of the list, and removes it
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+char *Ringbuf_Pop_Front(RING_BUFFER *b)
+{
+ char *data = NULL; // return value
+
+ if (b->count)
+ {
+ data = &(b->data[b->head * b->element_size]);
+ b->head++;
+ if (b->head >= b->element_count)
+ b->head = 0;
+ b->count--;
+ }
+
+ return data;
+}
+
+/****************************************************************************
+* DESCRIPTION: Adds an element of data to the ring buffer
+* RETURN: TRUE on succesful add, FALSE if not added
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+bool Ringbuf_Put(
+ RING_BUFFER *b, // ring buffer structure
+ char *data_element) // one element to add to the ring
+{
+ bool status = FALSE; // return value
+ unsigned offset = 0; // offset into array of data
+ char *ring_data = NULL; // used to help point ring data
+ unsigned i; // loop counter
+
+ if (b && data_element)
+ {
+ // limit the amount of data that we accept
+ if (b->count < b->element_count)
+ {
+ offset = b->head + b->count;
+ if (offset >= b->element_count)
+ offset -= b->element_count;
+ ring_data = b->data + offset * b->element_size;
+ for(i = 0; i < b->element_size; i++)
+ {
+ ring_data[i] = data_element[i];
+ }
+ b->count++;
+ status = TRUE;
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+* DESCRIPTION: Configures the ring buffer
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+void Ringbuf_Init(
+ RING_BUFFER *b, // ring buffer structure
+ char *data, // data block or array of data
+ unsigned element_size, // size of one element in the data block
+ unsigned element_count) // number of elements in the data block
+{
+ b->head = 0;
+ b->count = 0;
+ b->data = data;
+ b->element_size = element_size;
+ b->element_count = element_count;
+
+ return;
+}
+
+#ifdef TEST
+#include
+#include
+
+#include "ctest.h"
+
+// test the FIFO
+#define RING_BUFFER_DATA_SIZE 5
+#define RING_BUFFER_SIZE 16
+void testRingBuf(Test* pTest)
+{
+ RING_BUFFER test_buffer;
+ char data_store[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE];
+ char data[RING_BUFFER_DATA_SIZE];
+ char *test_data;
+ unsigned index;
+ unsigned data_index;
+ unsigned count;
+ unsigned dummy;
+ bool status;
+
+ Ringbuf_Init(&test_buffer,data_store,RING_BUFFER_DATA_SIZE,RING_BUFFER_SIZE);
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = data_index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == data[data_index]);
+ }
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == data[data_index]);
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ // fill to max
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+ }
+ // verify actions on full buffer
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == FALSE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+ }
+
+ // check buffer full
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == index);
+ }
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == index);
+ }
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ // test the ring around the buffer
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (count = 1; count < 4; count++)
+ {
+ dummy = index * count;
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = dummy;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ }
+
+ for (count = 1; count < 4; count++)
+ {
+ dummy = index * count;
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == dummy);
+ }
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == dummy);
+ }
+ }
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+
+ return;
+}
+
+#ifdef TEST_RINGBUF
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("ringbuf", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testRingBuf);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
+#endif
+
diff --git a/bacnet-stack/.svn/text-base/ringbuf.h.svn-base b/bacnet-stack/.svn/text-base/ringbuf.h.svn-base
new file mode 100644
index 00000000..68b9ae34
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/ringbuf.h.svn-base
@@ -0,0 +1,66 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 by Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+/* Functional Description: Generic ring buffer library for deeply
+ embedded system. See the unit tests for usage examples. */
+
+#ifndef RINGBUF_H
+#define RINGBUF_H
+
+#include "stdint.h"
+
+struct ring_buffer_t
+{
+ char *data; // block of memory or array of data
+ unsigned element_size; // how many bytes for each chunk
+ unsigned element_count; // number of chunks of data
+ unsigned head; // first chunk of data
+ unsigned count; // number of chunks in use
+};
+typedef struct ring_buffer_t RING_BUFFER;
+
+extern bool Ringbuf_Empty(RING_BUFFER const *b);
+extern char *Ringbuf_Get_Front(RING_BUFFER const *b);
+extern char *Ringbuf_Pop_Front(RING_BUFFER *b);
+extern bool Ringbuf_Put(
+ RING_BUFFER *b, // ring buffer structure
+ char *data_element); // one element to add to the ring
+extern void Ringbuf_Init(
+ RING_BUFFER *b, // ring buffer structure
+ char *data, // data block or array of data
+ unsigned element_size, // size of one element in the data block
+ unsigned element_count); // number of elements in the data block
+
+#endif
diff --git a/bacnet-stack/.svn/text-base/ringbuf.mak.svn-base b/bacnet-stack/.svn/text-base/ringbuf.mak.svn-base
new file mode 100644
index 00000000..26c685c4
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/ringbuf.mak.svn-base
@@ -0,0 +1,29 @@
+#Makefile to build ringbuf tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_RINGBUF -g
+
+OBJS = ringbuf.o test/ctest.o
+
+TARGET = ringbuf
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/.svn/text-base/rs485.h.svn-base b/bacnet-stack/.svn/text-base/rs485.h.svn-base
new file mode 100644
index 00000000..59b0c55f
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/rs485.h.svn-base
@@ -0,0 +1,53 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#ifndef RS485_H
+#define RS485_H
+
+#include
+#include "mstp.h"
+
+void RS485_Send_Frame(
+ struct mstp_port_struct_t *mstp_port, // port specific data
+ 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
+ unsigned data_len); // number of bytes of data (up to 501)
+
+void RS485_Check_UART_Data(
+ struct mstp_port_struct_t *mstp_port); // port specific data
+
+#endif
diff --git a/bacnet-stack/.svn/text-base/stdbool.h.svn-base b/bacnet-stack/.svn/text-base/stdbool.h.svn-base
new file mode 100644
index 00000000..29b9a5e4
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/stdbool.h.svn-base
@@ -0,0 +1,28 @@
+#ifndef STDBOOL_H
+#define STDBOOL_H
+
+// C99 Boolean types for compilers without C99 support
+
+#ifndef __cplusplus
+ typedef int _Bool;
+ #ifndef bool
+ #define bool _Bool
+ #endif
+ #ifndef true
+ #define true 1
+ #endif
+ #ifndef false
+ #define false 0
+ #endif
+ #define __bool_true_false_are_defined 1
+#endif
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+#endif
diff --git a/bacnet-stack/.svn/text-base/stdint.h.svn-base b/bacnet-stack/.svn/text-base/stdint.h.svn-base
new file mode 100644
index 00000000..b66446d4
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/stdint.h.svn-base
@@ -0,0 +1,26 @@
+// Defines the standard integer types that are used in code
+// for the x86 processor and Borland Compiler
+
+#ifndef STDINT_H
+#define STDINT_H
+
+#include
+
+#define TRUE 1
+#define FALSE 0
+
+#define MSB 7
+#define LSB 0
+
+typedef int bool;
+typedef unsigned char uint8_t; // 1 byte 0 to 255
+typedef signed char int8_t; // 1 byte -127 to 127
+typedef unsigned short uint16_t; // 2 bytes 0 to 65535
+typedef signed short int16_t; // 2 bytes -32767 to 32767
+//typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215
+typedef unsigned long uint32_t; // 4 bytes 0 to 4294967295
+typedef signed long int32_t; // 4 bytes -2147483647 to 2147483647
+// typedef signed long long int64_t;
+// typedef unsigned long long uint64_t;
+
+#endif // STDINT_H
diff --git a/bacnet-stack/.svn/text-base/test.sh.svn-base b/bacnet-stack/.svn/text-base/test.sh.svn-base
new file mode 100644
index 00000000..4e7739f1
--- /dev/null
+++ b/bacnet-stack/.svn/text-base/test.sh.svn-base
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Unit tests builder / runner for this project
+
+rm test.log
+touch test.log
+
+make -f crc.mak
+./crc >> test.log
+make -f crc.mak clean
+
+make -f ringbuf.mak
+./ringbuf >> test.log
+make -f ringbuf.mak clean
+
+make -f mstp.mak
+./mstp >> test.log
+make -f mstp.mak clean
+
+
diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile
new file mode 100644
index 00000000..11fde245
--- /dev/null
+++ b/bacnet-stack/Makefile
@@ -0,0 +1,29 @@
+#Makefile to build BACnet Application
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -g
+
+OBJS = main.o mstp.o crc.o ringbuf.o ports/linux/rs485.o
+
+TARGET = bacnet
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak ports/linux/*.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/bytes.h b/bacnet-stack/bytes.h
new file mode 100644
index 00000000..97dce0bd
--- /dev/null
+++ b/bacnet-stack/bytes.h
@@ -0,0 +1,42 @@
+// Defines the bit/byte/word/long conversions that are used in code
+
+#ifndef BYTES_H
+#define BYTES_H
+
+#include
+
+#ifndef LO_NIB
+ #define LO_NIB(b) ((b) & 0xF)
+#endif
+
+#ifndef HI_NIB
+ #define HI_NIB(b) ((b) >> 4)
+#endif
+
+#ifndef LO_BYTE
+ #define LO_BYTE(w) ((uint8_t)(w))
+#endif
+
+#ifndef HI_BYTE
+ #define HI_BYTE(w) ((uint8_t)((uint16_t)(w) >> 8))
+#endif
+
+#ifndef LO_WORD
+ #define LO_WORD(x) ((uint16_t)(x))
+#endif
+
+#ifndef HI_WORD
+ #define HI_WORD(x) ((uint16_t)((uint32_t)(x) >> 16))
+#endif
+
+#ifndef MAKE_WORD
+ #define MAKE_WORD(lo,hi) \
+ ((uint16_t)(((uint8_t)(lo))|(((uint16_t)((uint8_t)(hi)))<<8)))
+#endif
+
+#ifndef MAKE_LONG
+ #define MAKE_LONG(lo,hi) \
+ ((uint32_t)(((uint16_t)(lo))|(((uint32_t)((uint16_t)(hi)))<<16)))
+#endif
+
+#endif // end of header file
diff --git a/bacnet-stack/crc.c b/bacnet-stack/crc.c
new file mode 100644
index 00000000..6c429155
--- /dev/null
+++ b/bacnet-stack/crc.c
@@ -0,0 +1,153 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+#include
+#include
+#include
+
+// Accumulate "dataValue" into the CRC in crcValue.
+// Return value is updated CRC
+//
+// The ^ operator means exclusive OR.
+// Note: This function is copied directly from the BACnet standard.
+uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue)
+{
+ uint16_t crc;
+
+ crc = crcValue ^ dataValue; /* XOR C7..C0 with D7..D0 */
+
+ /* Exclusive OR the terms in the table (top down) */
+ crc = crc ^ (crc << 1) ^ (crc << 2) ^ (crc << 3)
+ ^ (crc << 4) ^ (crc << 5) ^ (crc << 6)
+ ^ (crc << 7);
+
+ /* Combine bits shifted out left hand end */
+ return (crc & 0xfe) ^ ((crc >> 8) & 1);
+}
+
+// Accumulate "dataValue" into the CRC in crcValue.
+// Return value is updated CRC
+//
+// The ^ operator means exclusive OR.
+// Note: This function is copied directly from the BACnet standard.
+uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue)
+{
+ uint16_t crcLow;
+
+ crcLow = (crcValue & 0xff) ^ dataValue; /* XOR C7..C0 with D7..D0 */
+
+ /* Exclusive OR the terms in the table (top down) */
+ return (crcValue >>8) ^ (crcLow << 8) ^ (crcLow <<3)
+ ^ (crcLow <<12) ^ (crcLow >> 4)
+ ^ (crcLow & 0x0f) ^ ((crcLow & 0x0f) << 7);
+}
+
+#ifdef TEST
+#include
+#include
+#include "ctest.h"
+#include "bytes.h"
+
+// test from Annex G 1.0 of BACnet Standard
+void testCRC8(Test* pTest)
+{
+ uint8_t crc = 0xff; // accumulates the crc value
+ uint8_t frame_crc; // appended to the end of the frame
+
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x55);
+ crc = CRC_Calc_Header(0x10,crc);
+ ct_test(pTest,crc == 0xC2);
+ crc = CRC_Calc_Header(0x05,crc);
+ ct_test(pTest,crc == 0xBC);
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x95);
+ crc = CRC_Calc_Header(0x00,crc);
+ ct_test(pTest,crc == 0x73);
+ // send the ones complement of the CRC in place of
+ // the CRC, and the resulting CRC will always equal 0x55.
+ frame_crc = ~crc;
+ ct_test(pTest,frame_crc == 0x8C);
+ // use the ones complement value and the next to last CRC value
+ crc = CRC_Calc_Header(frame_crc,crc);
+ ct_test(pTest,crc == 0x55);
+}
+
+// test from Annex G 2.0 of BACnet Standard
+void testCRC16(Test* pTest)
+{
+ uint16_t crc = 0xffff;
+ uint16_t data_crc;
+
+ crc = CRC_Calc_Data(0x01,crc);
+ ct_test(pTest,crc == 0x1E0E);
+ crc = CRC_Calc_Data(0x22,crc);
+ ct_test(pTest,crc == 0xEB70);
+ crc = CRC_Calc_Data(0x30,crc);
+ ct_test(pTest,crc == 0x42EF);
+ // send the ones complement of the CRC in place of
+ // the CRC, and the resulting CRC will always equal 0xF0B8.
+ data_crc = ~crc;
+ ct_test(pTest,data_crc == 0xBD10);
+ crc = CRC_Calc_Data(LO_BYTE(data_crc),crc);
+ ct_test(pTest,crc == 0x0F3A);
+ crc = CRC_Calc_Data(HI_BYTE(data_crc),crc);
+ ct_test(pTest,crc == 0xF0B8);
+}
+
+#endif
+
+#ifdef TEST_CRC
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("crc", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testCRC8);
+ assert(rc);
+ rc = ct_addTestFunction(pTest, testCRC16);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
diff --git a/bacnet-stack/crc.h b/bacnet-stack/crc.h
new file mode 100644
index 00000000..afc0d6d4
--- /dev/null
+++ b/bacnet-stack/crc.h
@@ -0,0 +1,44 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+#ifndef CRC_H
+#define CRC_H
+
+#include
+#include
+
+uint8_t CRC_Calc_Header(uint8_t dataValue, uint8_t crcValue);
+uint16_t CRC_Calc_Data(uint8_t dataValue, uint16_t crcValue);
+
+#endif
diff --git a/bacnet-stack/crc.ide b/bacnet-stack/crc.ide
new file mode 100644
index 00000000..d7b2030c
Binary files /dev/null and b/bacnet-stack/crc.ide differ
diff --git a/bacnet-stack/crc.mak b/bacnet-stack/crc.mak
new file mode 100644
index 00000000..e5bb862f
--- /dev/null
+++ b/bacnet-stack/crc.mak
@@ -0,0 +1,29 @@
+#Makefile to build CRC tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_CRC -g
+
+OBJS = crc.o test/ctest.o
+
+TARGET = crc
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/main.c b/bacnet-stack/main.c
new file mode 100644
index 00000000..c54fdc5b
--- /dev/null
+++ b/bacnet-stack/main.c
@@ -0,0 +1,63 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#include
+#include
+#include "mstp.h"
+#include "bytes.h"
+#include "crc.h"
+#include "rs485.h"
+#include "ringbuf.h"
+
+void main(void)
+{
+ struct mstp_port_struct_t mstp_port; // port data
+ uint8_t my_mac = 0x05; // local MAC address
+
+ MSTP_Init(&mstp_port,my_mac);
+
+ // loop forever
+ for (;;)
+ {
+ // input
+ RS485_Check_UART_Data(&mstp_port);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // process
+
+ // output
+ MSTP_Master_Node_FSM(&mstp_port);
+
+ }
+}
diff --git a/bacnet-stack/mstp.c b/bacnet-stack/mstp.c
new file mode 100644
index 00000000..a1214073
--- /dev/null
+++ b/bacnet-stack/mstp.c
@@ -0,0 +1,1671 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2003 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// This clause describes a Master-Slave/Token-Passing (MS/TP) data link
+// protocol, which provides the same services to the network layer as
+// ISO 8802-2 Logical Link Control. It uses services provided by the
+// EIA-485 physical layer. Relevant clauses of EIA-485 are deemed to be
+// included in this standard by reference. The following hardware is assumed:
+// (a) A UART (Universal Asynchronous Receiver/Transmitter) capable of
+// transmitting and receiving eight data bits with one stop bit
+// and no parity.
+// (b) An EIA-485 transceiver whose driver may be disabled.
+// (c) A timer with a resolution of five milliseconds or less
+
+#include
+#include
+#include "mstp.h"
+#include "bytes.h"
+#include "crc.h"
+#include "rs485.h"
+#include "ringbuf.h"
+
+// MS/TP Frame Format
+// All frames are of the following format:
+//
+// Preamble: two octet preamble: X`55', X`FF'
+// Frame Type: one octet
+// Destination Address: one octet address
+// Source Address: one octet address
+// Length: two octets, most significant octet first, of the Data field
+// Header CRC: one octet
+// Data: (present only if Length is non-zero)
+// Data CRC: (present only if Length is non-zero) two octets,
+// least significant octet first
+// (pad): (optional) at most one octet of padding: X'FF'
+
+ // The number of tokens received or used before a Poll For Master cycle
+// is executed: 50.
+const unsigned Npoll = 50;
+
+// The number of retries on sending Token: 1.
+const unsigned Nretry_token = 1;
+
+// The minimum number of DataAvailable or ReceiveError events that must be
+// seen by a receiving node in order to declare the line "active": 4.
+const unsigned 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 unsigned Tframe_abort = 1 + ((1000 * 60) / 9600);
+
+// The maximum idle time a sending node may allow to elapse between octets
+// of a frame the node is transmitting: 20 bit times.
+const unsigned Tframe_gap = 20;
+
+// The time without a DataAvailable or ReceiveError event before declaration
+// of loss of token: 500 milliseconds.
+const unsigned Tno_token = 500;
+
+// The maximum time after the end of the stop bit of the final
+// octet of a transmitted frame before a node must disable its
+// EIA-485 driver: 15 bit times.
+const unsigned Tpostdrive = 15;
+
+// 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.
+const unsigned Treply_delay = 225;
+
+// 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.)
+const unsigned Treply_timeout = 255;
+
+// 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.
+const unsigned Troff = 30;
+
+// The width of the time slot within which a node may generate a token:
+// 10 milliseconds.
+const unsigned Tslot = 10;
+
+// The maximum time a node may wait after reception of the token or
+// a Poll For Master frame before sending the first octet of a frame:
+// 15 milliseconds.
+const unsigned Tusage_delay = 15;
+
+// 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.)
+const unsigned Tusage_timeout = 20;
+
+// Millisecond Timer - called every millisecond
+void MSTP_Millisecond_Timer(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->SilenceTimer < 255)
+ mstp_port->SilenceTimer++;
+ if (mstp_port->ReplyPostponedTimer < 255)
+ mstp_port->ReplyPostponedTimer++;
+
+ return;
+}
+
+void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *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:
+ // EatAnError
+ if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if (mstp_port->DataAvailable == TRUE)
+ {
+ // Preamble1
+ if (mstp_port->DataRegister == 0x55)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // receive the remainder of the frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
+ }
+ // EatAnOctet
+ else
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ }
+ break;
+ // In the PREAMBLE state, the node waits for the second octet of the preamble.
+ case MSTP_RECEIVE_STATE_PREAMBLE:
+ // Timeout
+ if (mstp_port->SilenceTimer > 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)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if (mstp_port->DataAvailable == TRUE)
+ {
+ // Preamble2
+ if (mstp_port->DataRegister == 0xFF)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ 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)
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the second preamble octet.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_PREAMBLE;
+ }
+ // NotPreamble
+ else
+ {
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ }
+ break;
+ // In the HEADER state, the node waits for the fixed message header.
+ case MSTP_RECEIVE_STATE_HEADER:
+ // Timeout
+ if (mstp_port->SilenceTimer > Tframe_abort)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+
+ // Error
+ else if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else if (mstp_port->DataAvailable == TRUE)
+ {
+ // FrameType
+ if (mstp_port->Index == 0)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->FrameType = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 1;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Destination
+ else if (mstp_port->Index == 1)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DestinationAddress = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 2;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Source
+ else if (mstp_port->Index == 2)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->SourceAddress = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 3;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Length1
+ else if (mstp_port->Index == 3)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataLength = mstp_port->DataRegister * 256;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 4;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // Length2
+ else if (mstp_port->Index == 4)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataLength += mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index = 5;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER;
+ }
+ // HeaderCRC
+ else if (mstp_port->Index == 5)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ mstp_port->HeaderCRC = CRC_Calc_Header(
+ mstp_port->DataRegister,
+ mstp_port->HeaderCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_HEADER_CRC;
+ }
+ // not per MS/TP standard, but it is a case not covered
+ else
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->EventCount++;
+ // indicate that an error has occurred during
+ // the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of a frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ break;
+ // In the HEADER_CRC state, the node validates the CRC on the fixed
+ // message header.
+ case MSTP_RECEIVE_STATE_HEADER_CRC:
+ // BadCRC
+ if (mstp_port->HeaderCRC != 0x55)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else
+ {
+ if ((mstp_port->DestinationAddress == mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS))
+ {
+ // FrameTooLong
+ if (mstp_port->DataLength > INPUT_BUFFER_SIZE)
+ {
+ // indicate that a frame with an illegal or
+ // unacceptable data length has been received
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // NoData
+ else if (mstp_port->DataLength == 0)
+ {
+ // 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;
+ }
+ // Data
+ else
+ {
+ mstp_port->Index = 0;
+ mstp_port->DataCRC = 0xFFFF;
+ // receive the data portion of the frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ }
+ // NotForUs
+ else
+ {
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ }
+ break;
+ // In the DATA state, the node waits for the data portion of a frame.
+ case MSTP_RECEIVE_STATE_DATA:
+ // Timeout
+ if (mstp_port->SilenceTimer > Tframe_abort)
+ {
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // Error
+ else if (mstp_port->ReceiveError == TRUE)
+ {
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->SilenceTimer = 0;
+ // indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ else if (mstp_port->DataAvailable == TRUE)
+ {
+ // DataOctet
+ if (mstp_port->Index < mstp_port->DataLength)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->InputBuffer[mstp_port->Index] = mstp_port->DataRegister;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index++;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ // CRC1
+ else if (mstp_port->Index == mstp_port->DataLength)
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->Index++; // Index now becomes the number of data octets
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA;
+ }
+ // CRC2
+ else if (mstp_port->Index == (mstp_port->DataLength + 1))
+ {
+ mstp_port->SilenceTimer = 0;
+ mstp_port->DataCRC = CRC_Calc_Data(
+ mstp_port->DataRegister,
+ mstp_port->DataCRC);
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_DATA_CRC;
+ }
+ }
+ break;
+ // In the DATA_CRC state, the node validates the CRC of the message data.
+ case MSTP_RECEIVE_STATE_DATA_CRC:
+ // GoodCRC
+ if (mstp_port->DataCRC == 0xF0B8)
+ {
+ // indicate the complete reception of a valid frame
+ mstp_port->ReceivedValidFrame = TRUE;
+
+ // now might be a good time to process the message or
+ // copy the data to a buffer so that we can process the message
+
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ // BadCRC
+ else
+ {
+ // to indicate that an error has occurred during the reception of a frame
+ mstp_port->ReceivedInvalidFrame = TRUE;
+ // wait for the start of the next frame.
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ }
+ break;
+ default:
+ // shouldn't get here - but if we do...
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ break;
+ }
+
+ return;
+}
+
+void MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port)
+{
+
+ switch (mstp_port->master_state)
+ {
+ case MSTP_MASTER_STATE_INITIALIZE:
+ // DoneInitializing
+ mstp_port->This_Station = 0; // FIXME: the node's station address
+ // 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->ReceivedValidFrame = FALSE;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ break;
+ // In the IDLE state, the node waits for a frame.
+ case MSTP_MASTER_STATE_IDLE:
+ // LostToken
+ if (mstp_port->SilenceTimer >= Tno_token)
+ {
+ // assume that the token has been lost
+ mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN;
+ }
+ // mstp_port->ReceivedInvalidFrame
+ else if (mstp_port->ReceivedInvalidFrame == TRUE)
+ {
+ // invalid frame was received
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedUnwantedFrame
+ else if (mstp_port->ReceivedValidFrame == TRUE)
+ {
+ if ((mstp_port->DestinationAddress != mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress != MSTP_BROADCAST_ADDRESS))
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // DestinationAddress is equal to 255 (broadcast) and
+ // FrameType has a value of Token, BACnet Data Expecting Reply, Test_Request,
+ // or a proprietary type known to this node that expects a reply
+ // (such frames may not be broadcast), or
+ else if ((mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TOKEN) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)))
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // FrameType has a value that indicates a standard or proprietary type
+ // that is not known to this node.
+ // FIXME: change this if you add a proprietary type
+ else if /*(*/(mstp_port->FrameType >= FRAME_TYPE_PROPRIETARY_MIN) /*&&*/
+ /*(FrameType <= FRAME_TYPE_PROPRIETARY_MAX))*/
+ /* unnecessary if FrameType is uint8_t with max of 255 */
+ {
+ // an unexpected or unwanted frame was received.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedToken
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_TOKEN))
+ {
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->FrameCount = 0;
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // ReceivedPFM
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_POLL_FOR_MASTER))
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedDataNoReply
+ // or a proprietary type known to this node that does not expect a reply
+ else if (((mstp_port->DestinationAddress == mstp_port->This_Station) ||
+ (mstp_port->DestinationAddress == MSTP_BROADCAST_ADDRESS)) &&
+ ((mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY) ||
+ // (mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE)))
+ {
+ // FIXME: indicate successful reception to the higher layers
+ // i.e. Process this frame!
+ mstp_port->ReceivedValidFrame = FALSE;
+ // wait for the next frame
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedDataNeedingReply
+ // or a proprietary type known to this node that expects a reply
+ else if ((mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ ((mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) ||
+ // (mstp_port->FrameType == FRAME_TYPE_PROPRIETARY) ||
+ (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)))
+ {
+ mstp_port->ReplyPostponedTimer = 0;
+ // indicate successful reception to the higher layers
+ // (management entity in the case of Test_Request);
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_ANSWER_DATA_REQUEST;
+ }
+ }
+ break;
+ // In the USE_TOKEN state, the node is allowed to send one or
+ // more data frames. These may be BACnet Data frames or
+ // proprietary frames.
+ case MSTP_MASTER_STATE_USE_TOKEN:
+ // NothingToSend
+ // FIXME: If there is no data frame awaiting transmission,
+ {
+ mstp_port->FrameCount = mstp_port->Nmax_info_frames;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // SendNoWait
+ // FIXME: If there is a frame awaiting transmission that
+ // is of type Test_Response, BACnet Data Not Expecting Reply,
+ // or a proprietary type that does not expect a reply,
+// {
+// // transmit the data frame
+// RS485_Send_Frame(?????????????);
+// FrameCount++;
+// mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+// }
+ // SendAndWait
+ // FIXME: If there is a frame awaiting transmission that is of
+ // type Test_Request, BACnet Data Expecting Reply, or
+ // a proprietary type that expects a reply,
+// {
+// // transmit the data frame
+// RS485_Send_Frame();
+// FrameCount++;
+// mstp_port->master_state = MSTP_MASTER_STATE_WAIT_FOR_REPLY;
+// }
+ // In the WAIT_FOR_REPLY state, the node waits for
+ // a reply from another node.
+ case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
+ // ReplyTimeout
+ if (mstp_port->SilenceTimer >= Treply_timeout)
+ {
+ // assume that the request has failed
+ mstp_port->FrameCount = mstp_port->Nmax_info_frames;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ // Any retry of the data frame shall await the next entry
+ // to the USE_TOKEN state. (Because of the length of the timeout,
+ // this transition will cause the token to be passed regardless
+ // of the initial value of FrameCount.)
+ }
+ // InvalidFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedInvalidFrame == TRUE))
+ {
+ // error in frame reception
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedReply
+ // or a proprietary type that indicates a reply
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE) ||
+ //(mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY)))
+ {
+ // FIXME: indicate successful reception to the higher layers
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedPostpone
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_REPLY_POSTPONED))
+ {
+ // FIXME: then the reply to the message has been postponed until a later time.
+ // So, what does this really mean?
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress != mstp_port->This_Station))
+ //the expected reply should not be broadcast)
+ {
+ // an unexpected frame was received
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // Synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->SilenceTimer < Treply_timeout) &&
+ (mstp_port->ReceivedValidFrame == TRUE) &&
+ ((mstp_port->FrameType == FRAME_TYPE_TEST_RESPONSE) ||
+ //(mstp_port->FrameType == FRAME_TYPE_PROPRIETARY_0) ||
+ (mstp_port->FrameType == FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY)))
+ {
+ // An unexpected frame was received.
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // Synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ break;
+ // The DONE_WITH_TOKEN state either sends another data frame,
+ // passes the token, or initiates a Poll For Master cycle.
+ case MSTP_MASTER_STATE_DONE_WITH_TOKEN:
+ // SendAnotherFrame
+ if (mstp_port->FrameCount < mstp_port->Nmax_info_frames)
+ {
+ // then this node may send another information frame
+ // before passing the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // mstp_port->SoleMaster
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount < Npoll) &&
+ (mstp_port->SoleMaster == TRUE))
+ {
+ // there are no other known master nodes to
+ // which the token may be sent (true master-slave operation).
+ mstp_port->FrameCount = 0;
+ mstp_port->TokenCount++;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // SendToken
+ else if (((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount < Npoll) &&
+ (mstp_port->SoleMaster == FALSE)) ||
+ // The comparison of NS and TS+1 eliminates the Poll For Master
+ // if there are no addresses between TS and NS, since there is no
+ // address at which a new master node may be found in that case.
+ (mstp_port->Next_Station ==
+ (uint8_t)((mstp_port->This_Station +1) % (mstp_port->Nmax_master + 1))))
+ {
+ mstp_port->TokenCount++;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SendMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) != mstp_port->Next_Station))
+ {
+ mstp_port->Poll_Station = (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1);
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ // ResetMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) == mstp_port->Next_Station) &&
+ (mstp_port->SoleMaster == FALSE))
+ {
+ mstp_port->Poll_Station = mstp_port->This_Station;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SoleMasterRestartMaintenancePFM
+ else if ((mstp_port->FrameCount >= mstp_port->Nmax_info_frames) &&
+ (mstp_port->TokenCount >= Npoll) &&
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) == mstp_port->Next_Station) &&
+ (mstp_port->SoleMaster == TRUE))
+ {
+ mstp_port->Poll_Station = (mstp_port->Next_Station +1) % (mstp_port->Nmax_master + 1);
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // no known successor node
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // find a new successor to TS
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ // The PASS_TOKEN state listens for a successor to begin using
+ // the token that this node has just attempted to pass.
+ case MSTP_MASTER_STATE_PASS_TOKEN:
+ // SawTokenUser
+ if ((mstp_port->SilenceTimer < Tusage_timeout) &&
+ (mstp_port->EventCount > Nmin_octets))
+ {
+ // Assume that a frame has been sent by the new token user.
+ // Enter the IDLE state to process the frame.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // RetrySendToken
+ else if ((mstp_port->SilenceTimer >= Tusage_timeout) &&
+ (mstp_port->RetryCount < Nretry_token))
+ {
+ mstp_port->RetryCount++;
+ // Transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->EventCount = 0;
+ // re-enter the current state to listen for NS
+ // to begin using the token.
+ }
+ // FindNewSuccessor
+ else if ((mstp_port->SilenceTimer >= Tusage_timeout) &&
+ (mstp_port->RetryCount >= Nretry_token))
+ {
+ // Assume that NS has failed.
+ mstp_port->Poll_Station = (mstp_port->Next_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // no known successor node
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // find a new successor to TS
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ break;
+ // The NO_TOKEN state is entered if mstp_port->SilenceTimer becomes greater
+ // than Tno_token, indicating that there has been no network activity
+ // for that period of time. The timeout is continued to determine
+ // whether or not this node may create a token.
+ case MSTP_MASTER_STATE_NO_TOKEN:
+ // SawFrame
+ if ((mstp_port->SilenceTimer < (Tno_token + (Tslot * mstp_port->This_Station))) &&
+ (mstp_port->EventCount > Nmin_octets))
+ {
+ // Some other node exists at a lower address.
+ // Enter the IDLE state to receive and process the incoming frame.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // GenerateToken
+ else if ((mstp_port->SilenceTimer >= (Tno_token + (Tslot * mstp_port->This_Station))) &&
+ (mstp_port->SilenceTimer < (Tno_token + (Tslot * (mstp_port->This_Station + 1)))))
+ {
+ // Assume that this node is the lowest numerical address
+ // on the network and is empowered to create a token.
+ mstp_port->Poll_Station = (mstp_port->This_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ // indicate that the next station is unknown
+ mstp_port->Next_Station = mstp_port->This_Station;
+ mstp_port->RetryCount = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->EventCount = 0;
+ // enter the POLL_FOR_MASTER state to find a new successor to TS.
+ mstp_port->master_state = MSTP_MASTER_STATE_POLL_FOR_MASTER;
+ }
+ break;
+ // In the POLL_FOR_MASTER state, the node listens for a reply to
+ // a previously sent Poll For Master frame in order to find
+ // a successor node.
+ case MSTP_MASTER_STATE_POLL_FOR_MASTER:
+ // ReceivedReplyToPFM
+ if ((mstp_port->ReceivedValidFrame == TRUE) &&
+ (mstp_port->DestinationAddress == mstp_port->This_Station) &&
+ (mstp_port->FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER))
+ {
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->Next_Station = mstp_port->SourceAddress;
+ mstp_port->EventCount = 0;
+ // Transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->Poll_Station = mstp_port->This_Station;
+ mstp_port->TokenCount = 0;
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // ReceivedUnexpectedFrame
+ else if ((mstp_port->ReceivedValidFrame == TRUE) &&
+ ((mstp_port->DestinationAddress != mstp_port->This_Station) ||
+ (mstp_port->FrameType != FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)))
+ {
+ // An unexpected frame was received.
+ // This may indicate the presence of multiple tokens.
+ mstp_port->ReceivedValidFrame = FALSE;
+ // enter the IDLE state to synchronize with the network.
+ // This action drops the token.
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ // mstp_port->SoleMaster
+ else if ((mstp_port->SoleMaster == TRUE) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // There was no valid reply to the periodic poll
+ // by the sole known master for other masters.
+ mstp_port->FrameCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ // DoneWithPFM
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station != mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // There was no valid reply to the maintenance
+ // poll for a master at address PS.
+ mstp_port->EventCount = 0;
+ // transmit a Token frame to NS
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TOKEN,
+ mstp_port->Next_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_PASS_TOKEN;
+ }
+ // SendNextPFM
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station == mstp_port->This_Station) && // no known successor node
+ ((uint8_t)((mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1)) != mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ mstp_port->Poll_Station =
+ (mstp_port->Poll_Station + 1) % (mstp_port->Nmax_master + 1);
+ // Transmit a Poll For Master frame to PS.
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_POLL_FOR_MASTER,
+ mstp_port->Poll_Station,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->RetryCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ // Re-enter the current state.
+ }
+ // DeclareSoleMaster
+ else if ((mstp_port->SoleMaster == FALSE) &&
+ (mstp_port->Next_Station == mstp_port->This_Station) && // no known successor node
+ ((uint8_t)((mstp_port->Poll_Station + 1) %
+ (mstp_port->Nmax_master + 1)) == mstp_port->This_Station) &&
+ ((mstp_port->SilenceTimer >= Tusage_timeout) ||
+ (mstp_port->ReceivedInvalidFrame == TRUE)))
+ {
+ // to indicate that this station is the only master
+ mstp_port->SoleMaster = TRUE;
+ mstp_port->FrameCount = 0;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->master_state = MSTP_MASTER_STATE_USE_TOKEN;
+ }
+ break;
+ // 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.
+ case MSTP_MASTER_STATE_ANSWER_DATA_REQUEST:
+ if (mstp_port->ReplyPostponedTimer <= Treply_delay)
+ {
+ // 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 RS485_Send_Frame to transmit the reply frame
+ // and enter the IDLE state to wait for the next frame.
+
+ // Test Request
+ // If a receiving node can successfully receive and return
+ // the information field, it shall do so. If it cannot receive
+ // and return the entire information field but can detect
+ // the reception of a valid Test_Request frame
+ // (for example, by computing the CRC on octets as
+ // they are received), then the receiving node shall discard
+ // the information field and return a Test_Response containing
+ // no information field. If the receiving node cannot detect
+ // the valid reception of frames with overlength information fields,
+ // then no response shall be returned.
+ if (mstp_port->FrameType == FRAME_TYPE_TEST_REQUEST)
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_TEST_RESPONSE,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ mstp_port->InputBuffer,
+ mstp_port->Index);
+ }
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+
+ //
+ // DeferredReply
+ // If no reply will be available from the higher layers
+ // within Treply_delay after the reception of the
+ // final octet of the requesting frame (the mechanism
+ // used to determine this is a local matter),
+ // then an immediate reply is not possible.
+ // Any reply shall wait until this node receives the token.
+ // Call RS485_Send_Frame to transmit a Reply Postponed frame,
+ // and enter the IDLE state.
+
+ else
+ {
+ RS485_Send_Frame(
+ mstp_port,
+ FRAME_TYPE_REPLY_POSTPONED,
+ mstp_port->SourceAddress,
+ mstp_port->This_Station,
+ NULL,0);
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ }
+ break;
+ default:
+ mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
+ break;
+ }
+
+ return;
+}
+
+void MSTP_Init(
+ struct mstp_port_struct_t *mstp_port,
+ uint8_t this_station_mac)
+{
+ int i; //loop counter
+
+ if (mstp_port)
+ {
+ mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
+ mstp_port->master_state = MSTP_MASTER_STATE_INITIALIZE;
+ mstp_port->ReceiveError = FALSE;
+ mstp_port->DataAvailable = FALSE;
+ mstp_port->DataRegister = 0;
+ mstp_port->DataCRC = 0;
+ mstp_port->DataCRC = 0;
+ mstp_port->DataLength = 0;
+ mstp_port->DestinationAddress = 0;
+ mstp_port->EventCount = 0;
+ mstp_port->FrameType = FRAME_TYPE_TOKEN;
+ mstp_port->FrameCount = 0;
+ mstp_port->HeaderCRC = 0;
+ mstp_port->Index = 0;
+ mstp_port->Index = 0;
+ for (i = 0; i < sizeof(mstp_port->InputBuffer); i++)
+ {
+ mstp_port->InputBuffer[i] = 0;
+ }
+ mstp_port->Next_Station = this_station_mac;
+ mstp_port->Poll_Station = this_station_mac;
+ mstp_port->ReceivedInvalidFrame = FALSE;
+ mstp_port->ReceivedValidFrame = FALSE;
+ mstp_port->RetryCount = 0;
+ mstp_port->SilenceTimer = 0;
+ mstp_port->ReplyPostponedTimer = 0;
+ mstp_port->SoleMaster = FALSE;
+ mstp_port->SourceAddress = 0;
+ mstp_port->TokenCount = 0;
+ mstp_port->This_Station = this_station_mac;
+ mstp_port->Nmax_info_frames = DEFAULT_MAX_INFO_FRAMES;
+ mstp_port->Nmax_master = DEFAULT_MAX_MASTER;
+ }
+}
+
+unsigned MSTP_Create_Frame(
+ uint8_t *buffer, // where frame is loaded
+ unsigned 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t crc8 = 0xFF; // used to calculate the crc value
+ uint16_t crc16 = 0xFFFF; // used to calculate the crc value
+ unsigned index = 0; // used to load the data portion of the frame
+
+ // not enough to do a header
+ if (buffer_len < 8)
+ return 0;
+
+ 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 / 256;
+ crc8 = CRC_Calc_Header(buffer[5],crc8);
+ buffer[6] = data_len % 256;
+ crc8 = CRC_Calc_Header(buffer[6],crc8);
+ buffer[7] = ~crc8;
+
+ index = 8;
+ while (data_len && data && (index < buffer_len))
+ {
+ buffer[index] = *data;
+ crc16 = CRC_Calc_Data(buffer[index],crc16);
+ data++;
+ index++;
+ data_len--;
+ }
+ // append the data CRC if necessary
+ if (index > 8)
+ {
+ if ((index + 2) <= buffer_len)
+ {
+ crc16 = ~crc16;
+ buffer[index] = LO_BYTE(crc16);
+ index++;
+ buffer[index] = HI_BYTE(crc16);
+ index++;
+ }
+ else
+ return 0;
+ }
+
+ return index; // returns the frame length
+}
+
+
+#ifdef TEST
+#include
+#include
+#include "ctest.h"
+
+// test stub functions
+void RS485_Send_Frame(
+ struct mstp_port_struct_t *mstp_port, // port specific data
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ (void)mstp_port;
+ (void)frame_type;
+ (void)destination;
+ (void)source;
+ (void)data;
+ (void)data_len;
+}
+
+#define RING_BUFFER_DATA_SIZE 1
+#define RING_BUFFER_SIZE INPUT_BUFFER_SIZE
+static RING_BUFFER Test_Buffer;
+static uint8_t Test_Buffer_Data[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE];
+static void Load_Input_Buffer(uint8_t *buffer,size_t len)
+{
+ static bool initialized = FALSE; // tracks our init
+
+
+ if (!initialized)
+ {
+ initialized = TRUE;
+ Ringbuf_Init(
+ &Test_Buffer,
+ (char *)Test_Buffer_Data,
+ RING_BUFFER_DATA_SIZE,
+ RING_BUFFER_SIZE);
+ }
+
+ // empty any the existing data
+ while (!Ringbuf_Empty(&Test_Buffer))
+ {
+ (void)Ringbuf_Pop_Front(&Test_Buffer);
+ }
+
+ if (buffer)
+ {
+ while (len)
+ {
+ (void)Ringbuf_Put(&Test_Buffer,(char *)buffer);
+ len--;
+ buffer++;
+ }
+ }
+}
+
+void RS485_Check_UART_Data(
+ struct mstp_port_struct_t *mstp_port) // port specific data
+{
+ char *data;
+ if (!Ringbuf_Empty(&Test_Buffer) && mstp_port &&
+ (mstp_port->DataAvailable == FALSE))
+ {
+ data = Ringbuf_Pop_Front(&Test_Buffer);
+ if (data)
+ {
+ mstp_port->DataRegister = *data;
+ mstp_port->DataAvailable = TRUE;
+ }
+ }
+}
+
+void testReceiveNodeFSM(Test* pTest)
+{
+ 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[INPUT_BUFFER_SIZE] = {0};
+ uint8_t data[INPUT_BUFFER_SIZE - 8 /*header*/ - 2 /*CRC*/] = {0};
+
+ MSTP_Init(&mstp_port,my_mac);
+
+ // check the receive error during idle
+ mstp_port.receive_state = MSTP_RECEIVE_STATE_IDLE;
+ mstp_port.ReceiveError = TRUE;
+ mstp_port.SilenceTimer = 255;
+ mstp_port.EventCount = 0;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for bad packet header
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x11;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header, but timeout
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // force the timeout
+ mstp_port.SilenceTimer = Tframe_abort + 1;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble, but receive error
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // force the error
+ mstp_port.ReceiveError = TRUE;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble1, but bad preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // no change of state if no data yet
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // repeated preamble1
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // repeated preamble1
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ // bad data
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x11;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble, but timeout in packet
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // force the timeout
+ mstp_port.SilenceTimer = Tframe_abort + 1;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+
+ // check for good packet header preamble, but error in packet
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // force the error
+ mstp_port.ReceiveError = TRUE;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceiveError == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // check for good packet header preamble
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x55;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_PREAMBLE);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ // preamble2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0xFF;
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 0);
+ ct_test(pTest,mstp_port.HeaderCRC == 0xFF);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // no change of state if no data yet
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 1);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,FrameType == FRAME_TYPE_TOKEN);
+ // Destination
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0x10;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 2);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DestinationAddress == 0x10);
+ // Source
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = my_mac;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 3);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.SourceAddress == my_mac);
+ // Length1 = length*256
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 4);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DataLength == 0);
+ // Length2
+ mstp_port.DataAvailable = TRUE;
+ mstp_port.DataRegister = 0;
+ HeaderCRC = CRC_Calc_Header(mstp_port.DataRegister,HeaderCRC);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 5);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER);
+ ct_test(pTest,mstp_port.DataLength == 0);
+ // HeaderCRC
+ mstp_port.DataAvailable = TRUE;
+ ct_test(pTest,HeaderCRC == 0x73); // per Annex G example
+ mstp_port.DataRegister = ~HeaderCRC; // one's compliment of CRC is sent
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ ct_test(pTest,mstp_port.Index == 5);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ ct_test(pTest,mstp_port.HeaderCRC == 0x55);
+ // NotForUs
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ Load_Input_Buffer(buffer,len);
+ for (i = 0; i < len; i++)
+ {
+ RS485_Check_UART_Data(&mstp_port);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == FALSE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == TRUE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // 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
+ ct_test(pTest,len > 0);
+ // 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);
+ EventCount++;
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.DataAvailable == FALSE);
+ ct_test(pTest,mstp_port.SilenceTimer == 0);
+ ct_test(pTest,mstp_port.EventCount == EventCount);
+ }
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_HEADER_CRC);
+ MSTP_Receive_Frame_FSM(&mstp_port);
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == TRUE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == FALSE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ // Data
+ mstp_port.ReceivedInvalidFrame = FALSE;
+ mstp_port.ReceivedValidFrame = FALSE;
+ memset(data,0,sizeof(data));
+ len = MSTP_Create_Frame(
+ buffer,
+ sizeof(buffer),
+ FRAME_TYPE_PROPRIETARY_MIN,
+ my_mac, // destination
+ my_mac, // source
+ data, // data
+ sizeof(data)); // data size
+ ct_test(pTest,len > 0);
+ 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);
+ }
+ ct_test(pTest,mstp_port.DataLength == sizeof(data));
+ ct_test(pTest,mstp_port.ReceivedInvalidFrame == FALSE);
+ ct_test(pTest,mstp_port.ReceivedValidFrame == TRUE);
+ ct_test(pTest,mstp_port.receive_state == MSTP_RECEIVE_STATE_IDLE);
+
+ return;
+}
+
+void testMasterNodeFSM(Test* pTest)
+{
+ struct mstp_port_struct_t mstp_port; // port data
+ uint8_t my_mac = 0x05; // local MAC address
+
+ MSTP_Init(&mstp_port,my_mac);
+ ct_test(pTest,mstp_port.master_state == MSTP_MASTER_STATE_INITIALIZE);
+
+}
+
+#endif
+
+#ifdef TEST_MSTP
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("mstp", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testReceiveNodeFSM);
+ assert(rc);
+ rc = ct_addTestFunction(pTest, testMasterNodeFSM);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
diff --git a/bacnet-stack/mstp.h b/bacnet-stack/mstp.h
new file mode 100644
index 00000000..6ec810c3
--- /dev/null
+++ b/bacnet-stack/mstp.h
@@ -0,0 +1,240 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#ifndef MSTP_H
+#define MSTP_H
+
+#include
+#include
+#include
+
+// The number of elements in the array InputBuffer[].
+#define INPUT_BUFFER_SIZE (501)
+
+// The value 255 is used to denote broadcast when used as a
+// destination address but is not allowed as a value for a station.
+#define MSTP_BROADCAST_ADDRESS 255
+
+// MS/TP Frame Type
+// Frame Types 8 through 127 are reserved by ASHRAE.
+#define FRAME_TYPE_TOKEN 0
+#define FRAME_TYPE_POLL_FOR_MASTER 1
+#define FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER 2
+#define FRAME_TYPE_TEST_REQUEST 3
+#define FRAME_TYPE_TEST_RESPONSE 4
+#define FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY 5
+#define FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY 6
+#define FRAME_TYPE_REPLY_POSTPONED 7
+// Frame Types 128 through 255: Proprietary Frames
+// 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.
+#define FRAME_TYPE_PROPRIETARY_MIN 128
+#define FRAME_TYPE_PROPRIETARY_MAX 255
+
+// receive FSM states
+typedef enum
+{
+ MSTP_RECEIVE_STATE_IDLE,
+ MSTP_RECEIVE_STATE_PREAMBLE,
+ MSTP_RECEIVE_STATE_HEADER,
+ MSTP_RECEIVE_STATE_HEADER_CRC,
+ MSTP_RECEIVE_STATE_DATA,
+ MSTP_RECEIVE_STATE_DATA_CRC,
+} MSTP_RECEIVE_STATE;
+
+// master node FSM states
+typedef enum
+{
+ MSTP_MASTER_STATE_INITIALIZE,
+ MSTP_MASTER_STATE_IDLE,
+ MSTP_MASTER_STATE_USE_TOKEN,
+ MSTP_MASTER_STATE_WAIT_FOR_REPLY,
+ MSTP_MASTER_STATE_DONE_WITH_TOKEN,
+ MSTP_MASTER_STATE_PASS_TOKEN,
+ MSTP_MASTER_STATE_NO_TOKEN,
+ MSTP_MASTER_STATE_POLL_FOR_MASTER,
+ MSTP_MASTER_STATE_ANSWER_DATA_REQUEST,
+} MSTP_MASTER_STATE;
+
+// data for a given MS/TP port
+struct mstp_port_struct_t
+{
+ MSTP_RECEIVE_STATE receive_state;
+ // When a master node is powered up or reset,
+ // it shall unconditionally enter the INITIALIZE state.
+ MSTP_MASTER_STATE master_state;
+
+ bool ReceiveError; // TRUE when error detected during Rx octet
+
+ bool DataAvailable; // There is data in the buffer
+
+ uint8_t DataRegister; // stores the latest data
+
+ // Used to accumulate the CRC on the data field of a frame.
+ uint16_t DataCRC;
+
+ // Used to store the data length of a received frame.
+ unsigned 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.
+ unsigned 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
+ // pass the token.
+ unsigned FrameCount;
+
+ // Used to accumulate the CRC on the header of a frame.
+ uint8_t HeaderCRC;
+
+ // Used as an index by the Receive State Machine, up to a maximum value of
+ // InputBufferSize.
+ unsigned 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.
+ // A smaller value for InputBufferSize may be used by some implementations.
+ uint8_t InputBuffer[INPUT_BUFFER_SIZE];
+
+ // "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.
+ uint8_t Poll_Station;
+
+ // 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 main
+ // state machine.
+ bool ReceivedInvalidFrame;
+
+ // A Boolean flag set to TRUE by the Receive State Machine if a valid frame
+ // is received. Set to FALSE by the main state machine.
+ bool ReceivedValidFrame;
+
+ // 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
+ unsigned SilenceTimer;
+
+ // 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.
+ unsigned ReplyPostponedTimer;
+
+ // A Boolean flag set to TRUE by the master machine if this node is the
+ // only known master node.
+ bool SoleMaster;
+
+ // 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.
+ 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.
+ 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.
+ unsigned 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.
+ unsigned Nmax_master;
+
+ // After receiving a frame this value will be TRUE until Tturnaround
+ // has expired
+ bool Turn_Around_Waiting;
+};
+
+#define DEFAULT_MAX_INFO_FRAMES 1
+#define DEFAULT_MAX_MASTER 127
+
+// 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
+#define Tturnaround 40;
+
+void MSTP_Millisecond_Timer(struct mstp_port_struct_t *mstp_port);
+void MSTP_Receive_Frame_FSM(struct mstp_port_struct_t *mstp_port);
+void MSTP_Master_Node_FSM(struct mstp_port_struct_t *mstp_port);
+
+unsigned MSTP_Create_Frame(
+ uint8_t *buffer, // where frame is loaded
+ unsigned 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
+ unsigned data_len); // number of bytes of data (up to 501)
+
+#endif
diff --git a/bacnet-stack/mstp.ide b/bacnet-stack/mstp.ide
new file mode 100644
index 00000000..986ae620
Binary files /dev/null and b/bacnet-stack/mstp.ide differ
diff --git a/bacnet-stack/mstp.mak b/bacnet-stack/mstp.mak
new file mode 100644
index 00000000..d93124de
--- /dev/null
+++ b/bacnet-stack/mstp.mak
@@ -0,0 +1,29 @@
+#Makefile to build BACnet MS/TP tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_MSTP -g
+
+OBJS = mstp.o crc.o ringbuf.o test/ctest.o
+
+TARGET = mstp
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/ports/.svn/README.txt b/bacnet-stack/ports/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/ports/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/ports/.svn/empty-file b/bacnet-stack/ports/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/ports/.svn/entries b/bacnet-stack/ports/.svn/entries
new file mode 100644
index 00000000..c9a3bd67
--- /dev/null
+++ b/bacnet-stack/ports/.svn/entries
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
diff --git a/bacnet-stack/ports/.svn/format b/bacnet-stack/ports/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/ports/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/ports/linux/.svn/README.txt b/bacnet-stack/ports/linux/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/ports/linux/.svn/empty-file b/bacnet-stack/ports/linux/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/ports/linux/.svn/entries b/bacnet-stack/ports/linux/.svn/entries
new file mode 100644
index 00000000..ac53629f
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/entries
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/bacnet-stack/ports/linux/.svn/format b/bacnet-stack/ports/linux/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/ports/linux/.svn/prop-base/readme.txt.svn-base b/bacnet-stack/ports/linux/.svn/prop-base/readme.txt.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/prop-base/readme.txt.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/linux/.svn/prop-base/rs485.c.svn-base b/bacnet-stack/ports/linux/.svn/prop-base/rs485.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/prop-base/rs485.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/linux/.svn/props/readme.txt.svn-work b/bacnet-stack/ports/linux/.svn/props/readme.txt.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/props/readme.txt.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/linux/.svn/props/rs485.c.svn-work b/bacnet-stack/ports/linux/.svn/props/rs485.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/props/rs485.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/linux/.svn/text-base/readme.txt.svn-base b/bacnet-stack/ports/linux/.svn/text-base/readme.txt.svn-base
new file mode 100644
index 00000000..c24be5af
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/text-base/readme.txt.svn-base
@@ -0,0 +1,2 @@
+This is a port to Linux for testing.
+The unit tests can be run via the test.sh script.
\ No newline at end of file
diff --git a/bacnet-stack/ports/linux/.svn/text-base/rs485.c.svn-base b/bacnet-stack/ports/linux/.svn/text-base/rs485.c.svn-base
new file mode 100644
index 00000000..710828f2
--- /dev/null
+++ b/bacnet-stack/ports/linux/.svn/text-base/rs485.c.svn-base
@@ -0,0 +1,115 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// The module handles sending data out the RS-485 port
+// and handles receiving data from the RS-485 port.
+// Customize this file for your specific hardware
+#include
+#include
+#include
+#include
+
+#include "mstp.h"
+
+// Transmits a Frame on the wire
+void RS485_Send_Frame(
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t buffer[INPUT_BUFFER_SIZE] = {0};
+ uint8_t *pbuf = NULL; // used for pointer arithmatic
+ unsigned len = 0; // number of bytes to send
+
+ // in order to avoid line contention
+ while (mstp_port->Turn_Around_Waiting)
+ {
+ // wait, yield, or whatever
+ }
+
+ // Disable the receiver, and enable the transmit line driver.
+
+ len = MSTP_Create_Frame(
+ buffer, // where frame is loaded
+ sizeof(buffer), // amount of space available
+ frame_type, // type of frame to send - see defines
+ destination, // destination address
+ source, // source address
+ data, // any data to be sent - may be null
+ data_len); // number of bytes of data (up to 501)
+
+ pbuf = &buffer[0];
+ while (len)
+ {
+ putc(*pbuf,stderr);
+ pbuf++;
+ len--;
+ }
+
+ // Wait until the final stop bit of the most significant CRC octet
+ // has been transmitted but not more than Tpostdrive.
+
+ // Disable the transmit line driver.
+
+ return;
+}
+
+// called by timer, interrupt(?) or other thread
+void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->ReceiveError == true)
+ {
+ // wait for state machine to clear this
+ }
+ // wait for state machine to read from the DataRegister
+ else if (mstp_port->DataAvailable == false)
+ {
+ // check for data
+
+ // if error,
+ // ReceiveError = TRUE;
+ // return;
+
+ mstp_port->DataRegister = 0; // FIXME: Get this data from UART or buffer
+
+ // if data is ready,
+ // DataAvailable = TRUE;
+ // return;
+ }
+}
+
diff --git a/bacnet-stack/ports/linux/readme.txt b/bacnet-stack/ports/linux/readme.txt
new file mode 100644
index 00000000..c24be5af
--- /dev/null
+++ b/bacnet-stack/ports/linux/readme.txt
@@ -0,0 +1,2 @@
+This is a port to Linux for testing.
+The unit tests can be run via the test.sh script.
\ No newline at end of file
diff --git a/bacnet-stack/ports/linux/rs485.c b/bacnet-stack/ports/linux/rs485.c
new file mode 100644
index 00000000..710828f2
--- /dev/null
+++ b/bacnet-stack/ports/linux/rs485.c
@@ -0,0 +1,115 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// The module handles sending data out the RS-485 port
+// and handles receiving data from the RS-485 port.
+// Customize this file for your specific hardware
+#include
+#include
+#include
+#include
+
+#include "mstp.h"
+
+// Transmits a Frame on the wire
+void RS485_Send_Frame(
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t buffer[INPUT_BUFFER_SIZE] = {0};
+ uint8_t *pbuf = NULL; // used for pointer arithmatic
+ unsigned len = 0; // number of bytes to send
+
+ // in order to avoid line contention
+ while (mstp_port->Turn_Around_Waiting)
+ {
+ // wait, yield, or whatever
+ }
+
+ // Disable the receiver, and enable the transmit line driver.
+
+ len = MSTP_Create_Frame(
+ buffer, // where frame is loaded
+ sizeof(buffer), // amount of space available
+ frame_type, // type of frame to send - see defines
+ destination, // destination address
+ source, // source address
+ data, // any data to be sent - may be null
+ data_len); // number of bytes of data (up to 501)
+
+ pbuf = &buffer[0];
+ while (len)
+ {
+ putc(*pbuf,stderr);
+ pbuf++;
+ len--;
+ }
+
+ // Wait until the final stop bit of the most significant CRC octet
+ // has been transmitted but not more than Tpostdrive.
+
+ // Disable the transmit line driver.
+
+ return;
+}
+
+// called by timer, interrupt(?) or other thread
+void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->ReceiveError == true)
+ {
+ // wait for state machine to clear this
+ }
+ // wait for state machine to read from the DataRegister
+ else if (mstp_port->DataAvailable == false)
+ {
+ // check for data
+
+ // if error,
+ // ReceiveError = TRUE;
+ // return;
+
+ mstp_port->DataRegister = 0; // FIXME: Get this data from UART or buffer
+
+ // if data is ready,
+ // DataAvailable = TRUE;
+ // return;
+ }
+}
+
diff --git a/bacnet-stack/ports/pic18/.svn/README.txt b/bacnet-stack/ports/pic18/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/ports/pic18/.svn/empty-file b/bacnet-stack/ports/pic18/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/ports/pic18/.svn/entries b/bacnet-stack/ports/pic18/.svn/entries
new file mode 100644
index 00000000..bfcc4103
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/entries
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/bacnet-stack/ports/pic18/.svn/format b/bacnet-stack/ports/pic18/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/ports/pic18/.svn/prop-base/rs485.c.svn-base b/bacnet-stack/ports/pic18/.svn/prop-base/rs485.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/prop-base/rs485.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/pic18/.svn/props/rs485.c.svn-work b/bacnet-stack/ports/pic18/.svn/props/rs485.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/props/rs485.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/ports/pic18/.svn/text-base/rs485.c.svn-base b/bacnet-stack/ports/pic18/.svn/text-base/rs485.c.svn-base
new file mode 100644
index 00000000..f5ae17d1
--- /dev/null
+++ b/bacnet-stack/ports/pic18/.svn/text-base/rs485.c.svn-base
@@ -0,0 +1,119 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// The module handles sending data out the RS-485 port
+// and handles receiving data from the RS-485 port.
+// Customize this file for your specific hardware
+#include
+#include
+#include
+
+#include "mstp.h"
+
+// Transmits a Frame on the wire
+void RS485_Send_Frame(
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t HeaderCRC; // used for running CRC calculation
+
+ (void)frame_type; // FIXME: temp until we implement this code
+ (void)destination; // FIXME: temp until we implement this code
+ (void)source; // FIXME: temp until we implement this code
+ (void)data; // FIXME: temp until we implement this code
+ (void)data_len; // FIXME: temp until we implement this code
+ // in order to avoid line contention
+ while (mstp_port->SilenceTimer < Tturnaround)
+ {
+ // wait, yield, or whatever
+ }
+
+ // Disable the receiver, and enable the transmit line driver.
+
+ // Transmit the preamble octets X'55', X'FF'.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ HeaderCRC = 0xFF;
+
+ // Transmit the Frame Type, Destination Address, Source Address,
+ // and Data Length octets. Accumulate each octet into HeaderCRC.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Transmit the ones-complement of HeaderCRC. Set SilenceTimer to zero.
+
+ // If there are data octets, initialize DataCRC to X'FFFF'.
+
+ // Transmit any data octets. Accumulate each octet into DataCRC.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Transmit the ones-complement of DataCRC, least significant octet first.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Wait until the final stop bit of the most significant CRC octet
+ // has been transmitted but not more than Tpostdrive.
+
+ // Disable the transmit line driver.
+
+ return;
+}
+
+// called by timer, interrupt(?) or other thread
+void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->ReceiveError == true)
+ {
+ // wait for state machine to clear this
+ }
+ // wait for state machine to read from the DataRegister
+ else if (mstp_port->DataAvailable == false)
+ {
+ // check for data
+
+ // if error,
+ // ReceiveError = TRUE;
+ // return;
+
+ mstp_port->DataRegister = 0; // FIXME: Get this data from UART or buffer
+
+ // if data is ready,
+ // DataAvailable = TRUE;
+ // return;
+ }
+}
+
diff --git a/bacnet-stack/ports/pic18/rs485.c b/bacnet-stack/ports/pic18/rs485.c
new file mode 100644
index 00000000..f5ae17d1
--- /dev/null
+++ b/bacnet-stack/ports/pic18/rs485.c
@@ -0,0 +1,119 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+// The module handles sending data out the RS-485 port
+// and handles receiving data from the RS-485 port.
+// Customize this file for your specific hardware
+#include
+#include
+#include
+
+#include "mstp.h"
+
+// Transmits a Frame on the wire
+void RS485_Send_Frame(
+ 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
+ unsigned data_len) // number of bytes of data (up to 501)
+{
+ uint8_t HeaderCRC; // used for running CRC calculation
+
+ (void)frame_type; // FIXME: temp until we implement this code
+ (void)destination; // FIXME: temp until we implement this code
+ (void)source; // FIXME: temp until we implement this code
+ (void)data; // FIXME: temp until we implement this code
+ (void)data_len; // FIXME: temp until we implement this code
+ // in order to avoid line contention
+ while (mstp_port->SilenceTimer < Tturnaround)
+ {
+ // wait, yield, or whatever
+ }
+
+ // Disable the receiver, and enable the transmit line driver.
+
+ // Transmit the preamble octets X'55', X'FF'.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ HeaderCRC = 0xFF;
+
+ // Transmit the Frame Type, Destination Address, Source Address,
+ // and Data Length octets. Accumulate each octet into HeaderCRC.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Transmit the ones-complement of HeaderCRC. Set SilenceTimer to zero.
+
+ // If there are data octets, initialize DataCRC to X'FFFF'.
+
+ // Transmit any data octets. Accumulate each octet into DataCRC.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Transmit the ones-complement of DataCRC, least significant octet first.
+ // As each octet is transmitted, set SilenceTimer to zero.
+
+ // Wait until the final stop bit of the most significant CRC octet
+ // has been transmitted but not more than Tpostdrive.
+
+ // Disable the transmit line driver.
+
+ return;
+}
+
+// called by timer, interrupt(?) or other thread
+void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
+{
+ if (mstp_port->ReceiveError == true)
+ {
+ // wait for state machine to clear this
+ }
+ // wait for state machine to read from the DataRegister
+ else if (mstp_port->DataAvailable == false)
+ {
+ // check for data
+
+ // if error,
+ // ReceiveError = TRUE;
+ // return;
+
+ mstp_port->DataRegister = 0; // FIXME: Get this data from UART or buffer
+
+ // if data is ready,
+ // DataAvailable = TRUE;
+ // return;
+ }
+}
+
diff --git a/bacnet-stack/ports/rtos32/.svn/README.txt b/bacnet-stack/ports/rtos32/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/ports/rtos32/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/ports/rtos32/.svn/empty-file b/bacnet-stack/ports/rtos32/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/ports/rtos32/.svn/entries b/bacnet-stack/ports/rtos32/.svn/entries
new file mode 100644
index 00000000..dc828c7b
--- /dev/null
+++ b/bacnet-stack/ports/rtos32/.svn/entries
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/bacnet-stack/ports/rtos32/.svn/format b/bacnet-stack/ports/rtos32/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/ports/rtos32/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/ringbuf.c b/bacnet-stack/ringbuf.c
new file mode 100644
index 00000000..ea3745b4
--- /dev/null
+++ b/bacnet-stack/ringbuf.c
@@ -0,0 +1,287 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 by Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+/* Functional Description: Generic ring buffer library for deeply
+ embedded system. See the unit tests for usage examples. */
+
+#include "stdint.h"
+#include "ringbuf.h"
+
+/****************************************************************************
+* DESCRIPTION: Returns the empty/full status of the ring buffer
+* RETURN: TRUE if the ring buffer is empty, FALSE if it is not.
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+bool Ringbuf_Empty(RING_BUFFER const *b)
+{
+ return (b->count == 0);
+}
+
+/****************************************************************************
+* DESCRIPTION: Looks at the data from the head of the list without removing it
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+char *Ringbuf_Get_Front(RING_BUFFER const *b)
+{
+ return (b->count ? &(b->data[b->head * b->element_size]) : NULL);
+}
+
+/****************************************************************************
+* DESCRIPTION: Gets the data from the front of the list, and removes it
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+char *Ringbuf_Pop_Front(RING_BUFFER *b)
+{
+ char *data = NULL; // return value
+
+ if (b->count)
+ {
+ data = &(b->data[b->head * b->element_size]);
+ b->head++;
+ if (b->head >= b->element_count)
+ b->head = 0;
+ b->count--;
+ }
+
+ return data;
+}
+
+/****************************************************************************
+* DESCRIPTION: Adds an element of data to the ring buffer
+* RETURN: TRUE on succesful add, FALSE if not added
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+bool Ringbuf_Put(
+ RING_BUFFER *b, // ring buffer structure
+ char *data_element) // one element to add to the ring
+{
+ bool status = FALSE; // return value
+ unsigned offset = 0; // offset into array of data
+ char *ring_data = NULL; // used to help point ring data
+ unsigned i; // loop counter
+
+ if (b && data_element)
+ {
+ // limit the amount of data that we accept
+ if (b->count < b->element_count)
+ {
+ offset = b->head + b->count;
+ if (offset >= b->element_count)
+ offset -= b->element_count;
+ ring_data = b->data + offset * b->element_size;
+ for(i = 0; i < b->element_size; i++)
+ {
+ ring_data[i] = data_element[i];
+ }
+ b->count++;
+ status = TRUE;
+ }
+ }
+
+ return status;
+}
+
+/****************************************************************************
+* DESCRIPTION: Configures the ring buffer
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+void Ringbuf_Init(
+ RING_BUFFER *b, // ring buffer structure
+ char *data, // data block or array of data
+ unsigned element_size, // size of one element in the data block
+ unsigned element_count) // number of elements in the data block
+{
+ b->head = 0;
+ b->count = 0;
+ b->data = data;
+ b->element_size = element_size;
+ b->element_count = element_count;
+
+ return;
+}
+
+#ifdef TEST
+#include
+#include
+
+#include "ctest.h"
+
+// test the FIFO
+#define RING_BUFFER_DATA_SIZE 5
+#define RING_BUFFER_SIZE 16
+void testRingBuf(Test* pTest)
+{
+ RING_BUFFER test_buffer;
+ char data_store[RING_BUFFER_DATA_SIZE * RING_BUFFER_SIZE];
+ char data[RING_BUFFER_DATA_SIZE];
+ char *test_data;
+ unsigned index;
+ unsigned data_index;
+ unsigned count;
+ unsigned dummy;
+ bool status;
+
+ Ringbuf_Init(&test_buffer,data_store,RING_BUFFER_DATA_SIZE,RING_BUFFER_SIZE);
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = data_index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == data[data_index]);
+ }
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == data[data_index]);
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ // fill to max
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+ }
+ // verify actions on full buffer
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = index;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == FALSE);
+ ct_test(pTest,!Ringbuf_Empty(&test_buffer));
+ }
+
+ // check buffer full
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == index);
+ }
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == index);
+ }
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+ // test the ring around the buffer
+ for (index = 0; index < RING_BUFFER_SIZE; index++)
+ {
+ for (count = 1; count < 4; count++)
+ {
+ dummy = index * count;
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ data[data_index] = dummy;
+ }
+ status = Ringbuf_Put(&test_buffer, data);
+ ct_test(pTest,status == TRUE);
+ }
+
+ for (count = 1; count < 4; count++)
+ {
+ dummy = index * count;
+ test_data = Ringbuf_Get_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == dummy);
+ }
+
+ test_data = Ringbuf_Pop_Front(&test_buffer);
+ for (data_index = 0; data_index < RING_BUFFER_DATA_SIZE; data_index++)
+ {
+ ct_test(pTest,test_data[data_index] == dummy);
+ }
+ }
+ }
+ ct_test(pTest,Ringbuf_Empty(&test_buffer));
+
+
+ return;
+}
+
+#ifdef TEST_RINGBUF
+int main(void)
+{
+ Test *pTest;
+ bool rc;
+
+ pTest = ct_create("ringbuf", NULL);
+
+ /* individual tests */
+ rc = ct_addTestFunction(pTest, testRingBuf);
+ assert(rc);
+
+ ct_setStream(pTest, stdout);
+ ct_run(pTest);
+ (void)ct_report(pTest);
+
+ ct_destroy(pTest);
+
+ return 0;
+}
+#endif
+#endif
+
diff --git a/bacnet-stack/ringbuf.h b/bacnet-stack/ringbuf.h
new file mode 100644
index 00000000..68b9ae34
--- /dev/null
+++ b/bacnet-stack/ringbuf.h
@@ -0,0 +1,66 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 by Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+/* Functional Description: Generic ring buffer library for deeply
+ embedded system. See the unit tests for usage examples. */
+
+#ifndef RINGBUF_H
+#define RINGBUF_H
+
+#include "stdint.h"
+
+struct ring_buffer_t
+{
+ char *data; // block of memory or array of data
+ unsigned element_size; // how many bytes for each chunk
+ unsigned element_count; // number of chunks of data
+ unsigned head; // first chunk of data
+ unsigned count; // number of chunks in use
+};
+typedef struct ring_buffer_t RING_BUFFER;
+
+extern bool Ringbuf_Empty(RING_BUFFER const *b);
+extern char *Ringbuf_Get_Front(RING_BUFFER const *b);
+extern char *Ringbuf_Pop_Front(RING_BUFFER *b);
+extern bool Ringbuf_Put(
+ RING_BUFFER *b, // ring buffer structure
+ char *data_element); // one element to add to the ring
+extern void Ringbuf_Init(
+ RING_BUFFER *b, // ring buffer structure
+ char *data, // data block or array of data
+ unsigned element_size, // size of one element in the data block
+ unsigned element_count); // number of elements in the data block
+
+#endif
diff --git a/bacnet-stack/ringbuf.mak b/bacnet-stack/ringbuf.mak
new file mode 100644
index 00000000..26c685c4
--- /dev/null
+++ b/bacnet-stack/ringbuf.mak
@@ -0,0 +1,29 @@
+#Makefile to build ringbuf tests
+CC = gcc
+BASEDIR = .
+#CFLAGS = -Wall -I.
+# -g for debugging with gdb
+#CFLAGS = -Wall -I. -g
+CFLAGS = -Wall -I. -Itest -DTEST -DTEST_RINGBUF -g
+
+OBJS = ringbuf.o test/ctest.o
+
+TARGET = ringbuf
+
+all: ${TARGET}
+
+${TARGET}: ${OBJS}
+ ${CC} -o $@ ${OBJS}
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -rf core ${TARGET} $(OBJS) *.bak *.1 *.ini
+
+include: .depend
+
diff --git a/bacnet-stack/rs485.h b/bacnet-stack/rs485.h
new file mode 100644
index 00000000..59b0c55f
--- /dev/null
+++ b/bacnet-stack/rs485.h
@@ -0,0 +1,53 @@
+/*####COPYRIGHTBEGIN####
+ -------------------------------------------
+ Copyright (C) 2004 Steve Karg
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to:
+ The Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307
+ USA.
+
+ As a special exception, if other files instantiate templates or
+ use macros or inline functions from this file, or you compile
+ this file and link it with other works to produce a work based
+ on this file, this file does not by itself cause the resulting
+ work to be covered by the GNU General Public License. However
+ the source code for this file must still be made available in
+ accordance with section (3) of the GNU General Public License.
+
+ This exception does not invalidate any other reasons why a work
+ based on this file might be covered by the GNU General Public
+ License.
+ -------------------------------------------
+####COPYRIGHTEND####*/
+
+#ifndef RS485_H
+#define RS485_H
+
+#include
+#include "mstp.h"
+
+void RS485_Send_Frame(
+ struct mstp_port_struct_t *mstp_port, // port specific data
+ 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
+ unsigned data_len); // number of bytes of data (up to 501)
+
+void RS485_Check_UART_Data(
+ struct mstp_port_struct_t *mstp_port); // port specific data
+
+#endif
diff --git a/bacnet-stack/stdbool.h b/bacnet-stack/stdbool.h
new file mode 100644
index 00000000..29b9a5e4
--- /dev/null
+++ b/bacnet-stack/stdbool.h
@@ -0,0 +1,28 @@
+#ifndef STDBOOL_H
+#define STDBOOL_H
+
+// C99 Boolean types for compilers without C99 support
+
+#ifndef __cplusplus
+ typedef int _Bool;
+ #ifndef bool
+ #define bool _Bool
+ #endif
+ #ifndef true
+ #define true 1
+ #endif
+ #ifndef false
+ #define false 0
+ #endif
+ #define __bool_true_false_are_defined 1
+#endif
+
+#ifndef FALSE
+ #define FALSE 0
+#endif
+
+#ifndef TRUE
+ #define TRUE 1
+#endif
+
+#endif
diff --git a/bacnet-stack/stdint.h b/bacnet-stack/stdint.h
new file mode 100644
index 00000000..b66446d4
--- /dev/null
+++ b/bacnet-stack/stdint.h
@@ -0,0 +1,26 @@
+// Defines the standard integer types that are used in code
+// for the x86 processor and Borland Compiler
+
+#ifndef STDINT_H
+#define STDINT_H
+
+#include
+
+#define TRUE 1
+#define FALSE 0
+
+#define MSB 7
+#define LSB 0
+
+typedef int bool;
+typedef unsigned char uint8_t; // 1 byte 0 to 255
+typedef signed char int8_t; // 1 byte -127 to 127
+typedef unsigned short uint16_t; // 2 bytes 0 to 65535
+typedef signed short int16_t; // 2 bytes -32767 to 32767
+//typedef unsigned short long uint24_t; // 3 bytes 0 to 16777215
+typedef unsigned long uint32_t; // 4 bytes 0 to 4294967295
+typedef signed long int32_t; // 4 bytes -2147483647 to 2147483647
+// typedef signed long long int64_t;
+// typedef unsigned long long uint64_t;
+
+#endif // STDINT_H
diff --git a/bacnet-stack/test.sh b/bacnet-stack/test.sh
new file mode 100755
index 00000000..4e7739f1
--- /dev/null
+++ b/bacnet-stack/test.sh
@@ -0,0 +1,19 @@
+#!/bin/sh
+# Unit tests builder / runner for this project
+
+rm test.log
+touch test.log
+
+make -f crc.mak
+./crc >> test.log
+make -f crc.mak clean
+
+make -f ringbuf.mak
+./ringbuf >> test.log
+make -f ringbuf.mak clean
+
+make -f mstp.mak
+./mstp >> test.log
+make -f mstp.mak clean
+
+
diff --git a/bacnet-stack/test/.svn/README.txt b/bacnet-stack/test/.svn/README.txt
new file mode 100644
index 00000000..271a8ce9
--- /dev/null
+++ b/bacnet-stack/test/.svn/README.txt
@@ -0,0 +1,2 @@
+This is a Subversion working copy administrative directory.
+Visit http://subversion.tigris.org/ for more information.
diff --git a/bacnet-stack/test/.svn/empty-file b/bacnet-stack/test/.svn/empty-file
new file mode 100644
index 00000000..e69de29b
diff --git a/bacnet-stack/test/.svn/entries b/bacnet-stack/test/.svn/entries
new file mode 100644
index 00000000..4f4cef45
--- /dev/null
+++ b/bacnet-stack/test/.svn/entries
@@ -0,0 +1,28 @@
+
+
+
+
+
+
diff --git a/bacnet-stack/test/.svn/format b/bacnet-stack/test/.svn/format
new file mode 100644
index 00000000..b8626c4c
--- /dev/null
+++ b/bacnet-stack/test/.svn/format
@@ -0,0 +1 @@
+4
diff --git a/bacnet-stack/test/.svn/prop-base/ctest.c.svn-base b/bacnet-stack/test/.svn/prop-base/ctest.c.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/test/.svn/prop-base/ctest.c.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/test/.svn/prop-base/ctest.h.svn-base b/bacnet-stack/test/.svn/prop-base/ctest.h.svn-base
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/test/.svn/prop-base/ctest.h.svn-base
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/test/.svn/props/ctest.c.svn-work b/bacnet-stack/test/.svn/props/ctest.c.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/test/.svn/props/ctest.c.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/test/.svn/props/ctest.h.svn-work b/bacnet-stack/test/.svn/props/ctest.h.svn-work
new file mode 100644
index 00000000..dce2c1d5
--- /dev/null
+++ b/bacnet-stack/test/.svn/props/ctest.h.svn-work
@@ -0,0 +1 @@
+END
diff --git a/bacnet-stack/test/.svn/text-base/ctest.c.svn-base b/bacnet-stack/test/.svn/text-base/ctest.c.svn-base
new file mode 100644
index 00000000..e31e867e
--- /dev/null
+++ b/bacnet-stack/test/.svn/text-base/ctest.c.svn-base
@@ -0,0 +1,182 @@
+/* ctest.c: Implements the CTest Framework */
+
+#include "ctest.h"
+#include
+#include
+#include
+#include
+
+/* Number of tests to hold incrementally */
+enum {CHUNK = 10};
+
+Test* ct_create(const char* name, void (*init)(Test*))
+{
+ int backOutLevel = 0;
+ Test* pTest = malloc(sizeof(Test));
+ if (pTest)
+ {
+ pTest->nPass = pTest->nFail = pTest->nTests = 0;
+ pTest->pStream = stdout;
+
+ /* Allocate array of fptrs: */
+ assert(CHUNK);
+ pTest->pTestFuns = calloc(CHUNK, sizeof(TestFunc));
+ if (pTest->pTestFuns)
+ {
+ pTest->maxTests = CHUNK;
+ /* Allocate test name: */
+ assert(name);
+ pTest->name = malloc(strlen(name) + 1);
+ if (pTest->name)
+ strcpy(pTest->name, name);
+ else
+ ++backOutLevel;
+ }
+ else
+ ++backOutLevel;
+ }
+
+ /* Back-out allocations if memory failed: */
+ if (backOutLevel)
+ {
+ switch(backOutLevel)
+ {
+ case 2:
+ free(pTest->pTestFuns);
+ pTest->pTestFuns = NULL;
+ case 1:
+ free(pTest);
+ pTest = NULL;
+ }
+ }
+ else if (init)
+ {
+ assert(pTest);
+ init(pTest);
+ }
+ return pTest;
+}
+
+void ct_destroy(Test* pTest)
+{
+ assert(pTest);
+ assert(pTest->pTestFuns);
+ free(pTest->pTestFuns);
+ pTest->pTestFuns = NULL;
+ assert(pTest->name);
+ free(pTest->name);
+ pTest->name = NULL;
+ free(pTest);
+}
+
+bool ct_addTestFunction(Test* pTest, TestFunc tfun)
+{
+ assert(pTest);
+ assert(pTest->pTestFuns);
+ if (pTest->nTests == pTest->maxTests)
+ {
+ size_t newSize = pTest->nTests + CHUNK;
+ TestFunc* new_pTestFuns =
+ realloc(pTest->pTestFuns,
+ newSize * sizeof(TestFunc));
+ if (!new_pTestFuns)
+ return FALSE;
+ pTest->pTestFuns = new_pTestFuns;
+ pTest->maxTests += CHUNK;
+ }
+ assert(pTest->nTests < pTest->maxTests);
+ pTest->pTestFuns[pTest->nTests++] = tfun;
+ return TRUE;
+}
+
+void ct_setStream(Test* pTest, FILE* pStream)
+{
+ pTest->pStream = pStream;
+}
+
+FILE* ct_getStream(Test* pTest)
+{
+ return pTest->pStream;
+}
+
+long ct_report(Test* pTest)
+{
+ assert(pTest);
+ if (pTest->pStream)
+ {
+ fprintf(pTest->pStream,
+ "Test \"%s\":\n\tPassed: %ld\n\tFailed: %ld\n",
+ pTest->name, pTest->nPass, pTest->nFail);
+ }
+ return pTest->nFail;
+}
+
+
+void ct_succeed(Test* pTest)
+{
+ assert(pTest);
+ ++pTest->nPass;
+}
+
+void ct_do_test(Test* pTest, const char* str,
+ bool cond, const char* file, long line)
+{
+ assert(pTest);
+ if (!cond)
+ ct_do_fail(pTest, str, file, line);
+ else
+ ct_succeed(pTest);
+}
+
+void ct_do_fail(Test* pTest, const char* str,
+ const char* file, long line)
+{
+ assert(pTest);
+ ++pTest->nFail;
+ if (pTest->pStream)
+ {
+ fprintf(pTest->pStream,
+ "%s failure: (%s), %s (line %ld)\n",
+ pTest->name, str, file, line);
+ }
+}
+
+long ct_getNumPassed(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nPass;
+}
+
+long ct_getNumFailed(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nFail;
+}
+
+long ct_run(Test* pTest)
+{
+ size_t testNum;
+ assert(pTest);
+ for (testNum = 0; testNum < pTest->nTests; ++testNum)
+ pTest->pTestFuns[testNum](pTest);
+ return pTest->nFail;
+}
+
+void ct_reset(Test* pTest)
+{
+ assert(pTest);
+ pTest->nFail = pTest->nPass = 0;
+}
+
+const char* ct_getName(Test* pTest)
+{
+ assert(pTest);
+ return (pTest->name);
+}
+
+long ct_getNumTests(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nTests;
+}
+
diff --git a/bacnet-stack/test/.svn/text-base/ctest.h.svn-base b/bacnet-stack/test/.svn/text-base/ctest.h.svn-base
new file mode 100644
index 00000000..376a5c41
--- /dev/null
+++ b/bacnet-stack/test/.svn/text-base/ctest.h.svn-base
@@ -0,0 +1,61 @@
+/* ctest.h
+ *
+ * Defines a test framework for C projects.
+ */
+#ifndef CTEST_H
+#define CTEST_H
+
+#include
+#include
+
+#define ct_test(test, cond) \
+ ct_do_test(test, #cond, cond, __FILE__, __LINE__)
+#define ct_fail(test, str) \
+ ct_do_fail(test, str, __FILE__, __LINE__)
+
+typedef struct _Test Test;
+
+typedef void (*TestFunc)(Test*);
+
+struct _Test
+{
+ char* name;
+ FILE* pStream;
+ size_t nTests;
+ size_t maxTests;
+ TestFunc* pTestFuns;
+ long nPass;
+ long nFail;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Test* ct_create(const char* name, void (*init)(Test*));
+void ct_destroy(Test* pTest);
+
+const char* ct_getName(Test* pTest);
+long ct_getNumPassed(Test* pTest);
+long ct_getNumFailed(Test* pTest);
+long ct_getNumTests(Test* pTest);
+FILE* ct_getStream(Test* pTest);
+void ct_setStream(Test* pTest, FILE* stream);
+
+bool ct_addTestFunction(Test* pTest, TestFunc tfun);
+void ct_succeed(Test* pTest);
+long ct_run(Test* pTest);
+long ct_report(Test* pTest);
+void ct_reset(Test* pTest);
+
+/* Not intended for end-users: */
+void ct_do_test(Test* pTest, const char* str,
+ bool cond, const char* file, long line);
+void ct_do_fail(Test* pTest, const char* str,
+ const char* file, long line);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/bacnet-stack/test/ctest.c b/bacnet-stack/test/ctest.c
new file mode 100644
index 00000000..e31e867e
--- /dev/null
+++ b/bacnet-stack/test/ctest.c
@@ -0,0 +1,182 @@
+/* ctest.c: Implements the CTest Framework */
+
+#include "ctest.h"
+#include
+#include
+#include
+#include
+
+/* Number of tests to hold incrementally */
+enum {CHUNK = 10};
+
+Test* ct_create(const char* name, void (*init)(Test*))
+{
+ int backOutLevel = 0;
+ Test* pTest = malloc(sizeof(Test));
+ if (pTest)
+ {
+ pTest->nPass = pTest->nFail = pTest->nTests = 0;
+ pTest->pStream = stdout;
+
+ /* Allocate array of fptrs: */
+ assert(CHUNK);
+ pTest->pTestFuns = calloc(CHUNK, sizeof(TestFunc));
+ if (pTest->pTestFuns)
+ {
+ pTest->maxTests = CHUNK;
+ /* Allocate test name: */
+ assert(name);
+ pTest->name = malloc(strlen(name) + 1);
+ if (pTest->name)
+ strcpy(pTest->name, name);
+ else
+ ++backOutLevel;
+ }
+ else
+ ++backOutLevel;
+ }
+
+ /* Back-out allocations if memory failed: */
+ if (backOutLevel)
+ {
+ switch(backOutLevel)
+ {
+ case 2:
+ free(pTest->pTestFuns);
+ pTest->pTestFuns = NULL;
+ case 1:
+ free(pTest);
+ pTest = NULL;
+ }
+ }
+ else if (init)
+ {
+ assert(pTest);
+ init(pTest);
+ }
+ return pTest;
+}
+
+void ct_destroy(Test* pTest)
+{
+ assert(pTest);
+ assert(pTest->pTestFuns);
+ free(pTest->pTestFuns);
+ pTest->pTestFuns = NULL;
+ assert(pTest->name);
+ free(pTest->name);
+ pTest->name = NULL;
+ free(pTest);
+}
+
+bool ct_addTestFunction(Test* pTest, TestFunc tfun)
+{
+ assert(pTest);
+ assert(pTest->pTestFuns);
+ if (pTest->nTests == pTest->maxTests)
+ {
+ size_t newSize = pTest->nTests + CHUNK;
+ TestFunc* new_pTestFuns =
+ realloc(pTest->pTestFuns,
+ newSize * sizeof(TestFunc));
+ if (!new_pTestFuns)
+ return FALSE;
+ pTest->pTestFuns = new_pTestFuns;
+ pTest->maxTests += CHUNK;
+ }
+ assert(pTest->nTests < pTest->maxTests);
+ pTest->pTestFuns[pTest->nTests++] = tfun;
+ return TRUE;
+}
+
+void ct_setStream(Test* pTest, FILE* pStream)
+{
+ pTest->pStream = pStream;
+}
+
+FILE* ct_getStream(Test* pTest)
+{
+ return pTest->pStream;
+}
+
+long ct_report(Test* pTest)
+{
+ assert(pTest);
+ if (pTest->pStream)
+ {
+ fprintf(pTest->pStream,
+ "Test \"%s\":\n\tPassed: %ld\n\tFailed: %ld\n",
+ pTest->name, pTest->nPass, pTest->nFail);
+ }
+ return pTest->nFail;
+}
+
+
+void ct_succeed(Test* pTest)
+{
+ assert(pTest);
+ ++pTest->nPass;
+}
+
+void ct_do_test(Test* pTest, const char* str,
+ bool cond, const char* file, long line)
+{
+ assert(pTest);
+ if (!cond)
+ ct_do_fail(pTest, str, file, line);
+ else
+ ct_succeed(pTest);
+}
+
+void ct_do_fail(Test* pTest, const char* str,
+ const char* file, long line)
+{
+ assert(pTest);
+ ++pTest->nFail;
+ if (pTest->pStream)
+ {
+ fprintf(pTest->pStream,
+ "%s failure: (%s), %s (line %ld)\n",
+ pTest->name, str, file, line);
+ }
+}
+
+long ct_getNumPassed(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nPass;
+}
+
+long ct_getNumFailed(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nFail;
+}
+
+long ct_run(Test* pTest)
+{
+ size_t testNum;
+ assert(pTest);
+ for (testNum = 0; testNum < pTest->nTests; ++testNum)
+ pTest->pTestFuns[testNum](pTest);
+ return pTest->nFail;
+}
+
+void ct_reset(Test* pTest)
+{
+ assert(pTest);
+ pTest->nFail = pTest->nPass = 0;
+}
+
+const char* ct_getName(Test* pTest)
+{
+ assert(pTest);
+ return (pTest->name);
+}
+
+long ct_getNumTests(Test* pTest)
+{
+ assert(pTest);
+ return pTest->nTests;
+}
+
diff --git a/bacnet-stack/test/ctest.h b/bacnet-stack/test/ctest.h
new file mode 100644
index 00000000..376a5c41
--- /dev/null
+++ b/bacnet-stack/test/ctest.h
@@ -0,0 +1,61 @@
+/* ctest.h
+ *
+ * Defines a test framework for C projects.
+ */
+#ifndef CTEST_H
+#define CTEST_H
+
+#include
+#include
+
+#define ct_test(test, cond) \
+ ct_do_test(test, #cond, cond, __FILE__, __LINE__)
+#define ct_fail(test, str) \
+ ct_do_fail(test, str, __FILE__, __LINE__)
+
+typedef struct _Test Test;
+
+typedef void (*TestFunc)(Test*);
+
+struct _Test
+{
+ char* name;
+ FILE* pStream;
+ size_t nTests;
+ size_t maxTests;
+ TestFunc* pTestFuns;
+ long nPass;
+ long nFail;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+Test* ct_create(const char* name, void (*init)(Test*));
+void ct_destroy(Test* pTest);
+
+const char* ct_getName(Test* pTest);
+long ct_getNumPassed(Test* pTest);
+long ct_getNumFailed(Test* pTest);
+long ct_getNumTests(Test* pTest);
+FILE* ct_getStream(Test* pTest);
+void ct_setStream(Test* pTest, FILE* stream);
+
+bool ct_addTestFunction(Test* pTest, TestFunc tfun);
+void ct_succeed(Test* pTest);
+long ct_run(Test* pTest);
+long ct_report(Test* pTest);
+void ct_reset(Test* pTest);
+
+/* Not intended for end-users: */
+void ct_do_test(Test* pTest, const char* str,
+ bool cond, const char* file, long line);
+void ct_do_fail(Test* pTest, const char* str,
+ const char* file, long line);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif