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