diff --git a/CHANGELOG.md b/CHANGELOG.md index eb8a71aa..ef54c3d4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,13 +18,18 @@ The git repositories are hosted at the following sites: ### Added -- Added MSTP extended frames to src/datalink/mstp.c module used by mstpcap (#529) +- Added MSTP extended frames transmit to src/datalink/mstp.c + and ports/stm32f4xx/dlmstp.c modules (#531) +- Added MSTP extended frames to src/datalink/mstp.c module + used by mstpcap (#529) - Added menu to release script (#506) ### Changed -- Change SubscribeCOV Cancellations to always reply with Result+ (#526) -- Allow processing of Who-Has when DCC initiation is disabled +- Changed SubscribeCOV Cancellations to always reply with Result+ (#526) +- Changed Who-Has to process when DCC initiation is disabled +- Changed mstp.c external API to remove rs485.h dependency + for send frame. (#531) ### Fixed @@ -55,30 +60,38 @@ The git repositories are hosted at the following sites: ### Changed -- Changed WriteProperty string property checker to ignore length check with zero option.(#504) +- Changed WriteProperty string property checker to ignore length + check with zero option.(#504) ## [1.3.0] - 2023-09-28 ### Added - Added [feature#14] EventTimeStamp decoding from ReadPropertyMultiple app. (#503) -- Added Channel, Color, Color Temperature, & Lighting Output demo app with Blinkt! (#503) -- Added pipeline build of piface and blinkt apps with Raspberry Pi OS image. (#503) +- Added Channel, Color, Color Temperature, & Lighting Output demo + app with Blinkt! (#503) +- Added pipeline build of piface and blinkt apps with Raspberry Pi + OS image. (#503) - Added linear interpolation library functions used in fading and ramping. (#503) ### Changed -- Added Device timer API to feed elapsed milliseconds to children objects. (#503) -- Changed gitignore to ease the maintainenance of source files in app folder. (#503) -- Changed example server app device simulator to use mstimer instead of OS time. (#503) +- Added Device timer API to feed elapsed milliseconds to children + objects. (#503) +- Changed gitignore to ease the maintainenance of source files in + app folder. (#503) +- Changed example server app device simulator to use mstimer instead + of OS time. (#503) - Changed example channel object to be dynamically created or deleted. (#503) -- Changed example channel object to handle color & color temperature objects. (#503) +- Changed example channel object to handle color & color temperature + objects. (#503) ### Fixed - Fixed datetime decode of invalid application tag. (#495) - Fixed extraneous SO_BINDTODEVICE error message in Linux BIP. (#498) -- Fixed example Color, Color Temperature, and Lighting object fade, ramp, and step. (#503) +- Fixed example Color, Color Temperature, and Lighting object fade, + ramp, and step. (#503) - Fixed and secured BACnetXYcolor and ColorCommand codecs. (#503) ## [1.2.0] - 2023-09-11 diff --git a/apps/mstpcap/main.c b/apps/mstpcap/main.c index 1f2008f6..548fbbe7 100644 --- a/apps/mstpcap/main.c +++ b/apps/mstpcap/main.c @@ -430,6 +430,22 @@ uint16_t MSTP_Get_Send( return 0; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + (void)mstp_port; + (void)buffer; + (void)nbytes; +} + uint16_t MSTP_Get_Reply( volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ diff --git a/ports/linux/dlmstp.c b/ports/linux/dlmstp.c index c30dba45..28be145d 100644 --- a/ports/linux/dlmstp.c +++ b/ports/linux/dlmstp.c @@ -164,6 +164,7 @@ static uint32_t Timer_Silence(void *pArg) struct timespec now, diff; int32_t res; + (void)pArg; clock_gettime(CLOCK_MONOTONIC, &now); timespec_subtract(&diff, &now, &start); res = ((diff.tv_sec) * 1000 + (diff.tv_nsec) / 1000000); @@ -173,6 +174,7 @@ static uint32_t Timer_Silence(void *pArg) static void Timer_Silence_Reset(void *pArg) { + (void)pArg; clock_gettime(CLOCK_MONOTONIC, &start); } @@ -412,6 +414,20 @@ uint16_t MSTP_Get_Send( return pdu_len; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + RS485_Send_Frame(mstp_port, buffer, nbytes); +} + static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, uint16_t request_pdu_len, uint8_t src_address, diff --git a/ports/linux/dlmstp_linux.c b/ports/linux/dlmstp_linux.c index 6ea4ecb9..7a1dc266 100644 --- a/ports/linux/dlmstp_linux.c +++ b/ports/linux/dlmstp_linux.c @@ -384,6 +384,20 @@ uint16_t MSTP_Get_Send( return pdu_len; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + RS485_Send_Frame(mstp_port, buffer, nbytes); +} + bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, uint16_t request_pdu_len, uint8_t src_address, diff --git a/ports/linux/mstpsnap.c b/ports/linux/mstpsnap.c index 281ede4e..72df7c3b 100644 --- a/ports/linux/mstpsnap.c +++ b/ports/linux/mstpsnap.c @@ -100,6 +100,22 @@ uint16_t MSTP_Get_Send( return 0; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + (void)mstp_port; + (void)buffer; + (void)nbytes; +} + uint16_t MSTP_Get_Reply( volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ diff --git a/ports/linux/rx_fsm.c b/ports/linux/rx_fsm.c index 3b031894..e9a3d6f0 100644 --- a/ports/linux/rx_fsm.c +++ b/ports/linux/rx_fsm.c @@ -125,6 +125,22 @@ uint16_t MSTP_Get_Send( return 0; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + (void)mstp_port; + (void)buffer; + (void)nbytes; +} + uint16_t MSTP_Get_Reply( volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ diff --git a/ports/stm32f4xx/Makefile b/ports/stm32f4xx/Makefile index f8214644..112cb603 100644 --- a/ports/stm32f4xx/Makefile +++ b/ports/stm32f4xx/Makefile @@ -60,6 +60,7 @@ BACNET_SRC = \ $(BACNET_CORE)/bacint.c \ $(BACNET_CORE)/bacreal.c \ $(BACNET_CORE)/bacstr.c \ + $(BACNET_CORE)/datalink/cobs.c \ $(BACNET_CORE)/datalink/crc.c \ $(BACNET_CORE)/datetime.c \ $(BACNET_CORE)/dcc.c \ diff --git a/ports/stm32f4xx/bacnet.ewp b/ports/stm32f4xx/bacnet.ewp index 043296fb..20336db0 100644 --- a/ports/stm32f4xx/bacnet.ewp +++ b/ports/stm32f4xx/bacnet.ewp @@ -1138,6 +1138,9 @@ BACnet Datalink + + $PROJ_DIR$\..\..\src\bacnet\datalink\cobs.c + $PROJ_DIR$\..\..\src\bacnet\datalink\crc.c diff --git a/ports/stm32f4xx/dlmstp.c b/ports/stm32f4xx/dlmstp.c index 2f8e0683..105a75c1 100644 --- a/ports/stm32f4xx/dlmstp.c +++ b/ports/stm32f4xx/dlmstp.c @@ -37,10 +37,11 @@ #include #include #include "bacnet/bacdef.h" +#include "bacnet/datalink/cobs.h" +#include "bacnet/datalink/crc.h" #include "bacnet/datalink/dlmstp.h" #include "bacnet/datalink/mstpdef.h" #include "rs485.h" -#include "bacnet/datalink/crc.h" #include "bacnet/npdu.h" #include "bacnet/bits.h" #include "bacnet/bytes.h" @@ -363,6 +364,10 @@ static uint16_t MSTP_Create_Frame(uint8_t *buffer, uint16_t crc16 = 0xFFFF; /* used to load the data portion of the frame */ uint16_t index = 0; + /* length of the COBS encoded frame */ + uint16_t cobs_len; + /* true for COBS BACnet frames */ + bool cobs_bacnet_frame = false; /* not enough to do a header */ if (buffer_size < 8) @@ -381,25 +386,58 @@ static uint16_t MSTP_Create_Frame(uint8_t *buffer, buffer[6] = data_len & 0xFF; crc8 = CRC_Calc_Header(buffer[6], crc8); buffer[7] = ~crc8; - index = 8; - while (data_len && data && (index < buffer_size)) { - 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_size) { - crc16 = ~crc16; - buffer[index] = crc16 & 0xFF; /* LSB first */ - index++; - buffer[index] = crc16 >> 8; - index++; - } else + + if ((data_len > 501) || ((frame_type >= Nmin_COBS_type) && + (frame_type <= Nmax_COBS_type))) { + /* COBS encoded frame */ + if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) { + frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY; + cobs_bacnet_frame = true; + } else if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) { + frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY; + cobs_bacnet_frame = true; + } else if ((frame_type < Nmin_COBS_type) || + (frame_type > Nmax_COBS_type)) { + /* I'm sorry, Dave, I'm afraid I can't do that. */ return 0; + } + cobs_len = cobs_frame_encode(buffer, buffer_size, data, data_len); + /* check the results of COBs encoding for validity */ + if (cobs_bacnet_frame) { + if (cobs_len < Nmin_COBS_length_BACnet) { + return 0; + } else if (cobs_len > Nmax_COBS_length_BACnet) { + return 0; + } + } else { + if (cobs_len < Nmin_COBS_length) { + return 0; + } else if (cobs_len > Nmax_COBS_length) { + return 0; + } + } + /* for COBS, we must subtract two before use as the + MS/TP frame length field since CRC32 is 2 bytes longer + than CRC16 in original MSTP and non-COBS devices need + to be able to ingest the entire frame */ + index = index + cobs_len - 2; + } else if (data_len > 0) { + while (data_len && data && (index < buffer_size)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + if ((index + 2) > buffer_size) { + return 0; + } + crc16 = ~crc16; + buffer[index] = crc16 & 0xFF; /* LSB first */ + index++; + buffer[index] = crc16 >> 8; + index++; } return index; /* returns the frame length */ @@ -653,13 +691,37 @@ static void MSTP_Receive_Frame_FSM(void) } else if (Index == DataLength) { /* CRC1 */ DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } Index++; } else if (Index == (DataLength + 1)) { /* CRC2 */ DataCRC = CRC_Calc_Data(DataRegister, DataCRC); - /* STATE DATA CRC - no need for new state */ - /* indicate the complete reception of a valid frame */ - if (DataCRC == 0xF0B8) { + if (Index < InputBufferSize) { + InputBuffer[Index] = DataRegister; + } + if ((FrameType >= Nmin_COBS_type) && + (FrameType <= Nmax_COBS_type)) { + if (((Index+1) < InputBufferSize) && + cobs_frame_decode( + &InputBuffer[Index + 1], + InputBufferSize, + InputBuffer, Index + 1)) { + if (Receive_State == + MSTP_RECEIVE_STATE_DATA) { + /* ForUs */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs */ + MSTP_Flag.ReceivedValidFrameNotForUs = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + } else if (DataCRC == 0xF0B8) { + /* STATE DATA CRC - no need for new state */ + /* indicate the complete reception of a valid frame */ if (Receive_State == MSTP_RECEIVE_STATE_DATA) { /* ForUs */ MSTP_Flag.ReceivedValidFrame = true; @@ -678,7 +740,6 @@ static void MSTP_Receive_Frame_FSM(void) Frame_Rx_Callback(source, destination, frame, InputBuffer, DataLength); } - } else { MSTP_Flag.ReceivedInvalidFrame = true; } diff --git a/ports/win32/dlmstp-mm.c b/ports/win32/dlmstp-mm.c index 6b2dc7e0..b76cc4af 100644 --- a/ports/win32/dlmstp-mm.c +++ b/ports/win32/dlmstp-mm.c @@ -296,6 +296,20 @@ uint16_t MSTP_Get_Send( return pdu_len; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + RS485_Send_Frame(mstp_port, buffer, nbytes); +} + bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, uint16_t request_pdu_len, uint8_t src_address, diff --git a/ports/win32/dlmstp.c b/ports/win32/dlmstp.c index 49f9f91b..c8d489f2 100644 --- a/ports/win32/dlmstp.c +++ b/ports/win32/dlmstp.c @@ -281,6 +281,20 @@ uint16_t MSTP_Get_Send( return pdu_len; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + RS485_Send_Frame(mstp_port, buffer, nbytes); +} + static bool dlmstp_compare_data_expecting_reply(uint8_t *request_pdu, uint16_t request_pdu_len, uint8_t src_address, diff --git a/ports/win32/rx_fsm.c b/ports/win32/rx_fsm.c index 5003e8f9..d1c382fa 100644 --- a/ports/win32/rx_fsm.c +++ b/ports/win32/rx_fsm.c @@ -130,6 +130,22 @@ uint16_t MSTP_Get_Send( return 0; } +/** + * @brief Send an MSTP frame + * @param mstp_port - port specific data + * @param buffer - data to send + * @param nbytes - number of bytes of data to send + */ +void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes) +{ + (void)mstp_port; + (void)buffer; + (void)nbytes; +} + uint16_t MSTP_Get_Reply( volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) { /* milliseconds to wait for a packet */ diff --git a/src/bacnet/datalink/cobs.c b/src/bacnet/datalink/cobs.c index ae1ab53e..0166446b 100644 --- a/src/bacnet/datalink/cobs.c +++ b/src/bacnet/datalink/cobs.c @@ -77,7 +77,7 @@ uint32_t cobs_crc32k(uint8_t dataValue, uint32_t crc32kValue) /** * @brief Encodes 'length' octets of data located at 'from' and - * writes one or more COBS code blocks at 'to', removing + * writes one or more COBS code blocks at 'buffer', removing * any 0x55 octets that may present be in the encoded data. * @param buffer - encoded buffer * @param buffer_size - encoded buffer size @@ -154,7 +154,7 @@ size_t cobs_encode(uint8_t *buffer, } /** * @brief Encodes 'length' octets of client data located at 'from' and writes - * the COBS-encoded Encoded Data and Encoded CRC-32K fields at 'to'. + * the COBS-encoded Encoded Data and Encoded CRC-32K fields at 'buffer'. * @param buffer - encoded buffer * @param buffer_size - encoded buffer size * @param from - buffer to encode @@ -206,7 +206,7 @@ size_t cobs_frame_encode( /** * @brief Decodes 'length' octets of data located at 'from' and - * writes the original client data at 'to', restoring any + * writes the original client data at 'buffer', restoring any * 'mask' octets that may present in the encoded data. * @param buffer - decoded buffer * @param buffer_size - decoded buffer size @@ -266,7 +266,7 @@ size_t cobs_decode(uint8_t *buffer, /** * Decodes Encoded Data and Encoded CRC-32K fields at 'from' and - * writes the decoded client data at 'to'. Assumes 'length' contains + * writes the decoded client data at 'buffer'. Assumes 'length' contains * the actual combined length of these fields in octets (that is, the * MS/TP header Length field plus two). * @param buffer - decoded buffer diff --git a/src/bacnet/datalink/cobs.h b/src/bacnet/datalink/cobs.h index 917dd122..2861d9e8 100644 --- a/src/bacnet/datalink/cobs.h +++ b/src/bacnet/datalink/cobs.h @@ -33,6 +33,38 @@ /* inclusive extra bytes needed for APDU */ #define COBS_ENCODED_SIZE(a) ((a)+((a)/254)+1) + +/* The first COBS-encoded Frame Type value: 32. */ +#define Nmin_COBS_type 32 +/* The last COBS-encoded Frame Type value: 127. */ +#define Nmax_COBS_type 127 +/* The minimum valid Length value of any COBS-encoded frame: 5. + The theoretical minimum Length is calculated as follows: + COBS-encoded frames must contain at least one data octet. + The minimum COBS encoding overhead for the Encoded Data + field is one octet. The size of the Encoded CRC-32K field + is always five octets. Adding the lengths of these fields + and subtracting two (for backward compatibility) results + in a minimum Length value of five (1 + 1 + 5 - 2). In practice, + the minimum Length value is determined by the network-layer + client and is likely to be larger (e.g., for BACnet the minimum + Length is 502 + 1 + 3 = 506. */ +#define Nmin_COBS_length 5 +#define Nmin_COBS_length_BACnet 506 +/* The maximum valid Length value of any COBS-encoded frame: 2043. + The theoretical maximum Length is calculated as follows: + the largest data parameter that any future network client + may specify is 2032 octets (this is near the limit of the + CRC-32K's maximum error-detection capability). The worst-case + COBS encoding overhead for the Encoded Data field would be + 2032 / 254 = 8 octets. Adding in the size adjustment for the + Encoded CRC-32K results in a maximum Length value of + 2032 + 8 + 3 = 2043. In practice, the maximum Length value is + determined by the network-layer client and is likely to be + smaller (e.g., for BACnet the maximum Length is 1497 + 6 + 3 = 1506. */ +#define Nmax_COBS_length 2043 +#define Nmax_COBS_length_BACnet 1506 + #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ diff --git a/src/bacnet/datalink/mstp.c b/src/bacnet/datalink/mstp.c index 678e942d..c2c40dff 100644 --- a/src/bacnet/datalink/mstp.c +++ b/src/bacnet/datalink/mstp.c @@ -51,12 +51,11 @@ #if PRINT_ENABLED #include #endif +#include "bacnet/datalink/cobs.h" +#include "bacnet/datalink/crc.h" #include "bacnet/datalink/mstp.h" -#include "crc.h" -#include "rs485.h" #include "bacnet/datalink/mstptext.h" #include "bacnet/npdu.h" -#include "bacnet/datalink/cobs.h" #ifndef DEBUG_ENABLED #define DEBUG_ENABLED 0 @@ -199,23 +198,48 @@ void MSTP_Fill_BACnet_Address(BACNET_ADDRESS *src, uint8_t mstp_address) } } -uint16_t MSTP_Create_Frame(uint8_t *buffer, /* where frame is loaded */ - uint16_t buffer_len, /* amount of space available */ - uint8_t frame_type, /* type of frame to send - see defines */ - uint8_t destination, /* destination address */ - uint8_t source, /* source address */ - uint8_t *data, /* any data to be sent - may be null */ +/** + * @brief Create an MS/TP Frame + * + * All MS/TP frames are of the following format: + * Preamble: two octet preamble: X`55', X`FF' + * Frame Type: one octet + * Destination Address: one octet address + * Source Address: one octet address + * Length: two octets, most significant octet first, of the Data field + * Header CRC: one octet + * Data: (present only if Length is non-zero) + * Data CRC: (present only if Length is non-zero) two octets, + * least significant octet first + * (pad): (optional) at most one octet of padding: X'FF' + * + * @param buffer - where frame is loaded + * @param buffer_size - amount of space available in the buffer + * @param frame_type - type of frame to send - see defines + * @param destination - destination address + * @param source - source address + * @param data - any data to be sent - may be null + * @param data_len - number of bytes of data (up to 501) + * @return number of bytes encoded, or 0 on error + */ +uint16_t MSTP_Create_Frame(uint8_t *buffer, + uint16_t buffer_size, + uint8_t frame_type, + uint8_t destination, + uint8_t source, + uint8_t *data, uint16_t data_len) -{ /* number of bytes of data (up to 501) */ +{ uint8_t crc8 = 0xFF; /* used to calculate the crc value */ uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ uint16_t index = 0; /* used to load the data portion of the frame */ + uint16_t cobs_len; /* length of the COBS encoded frame */ + bool cobs_bacnet_frame = false; /* true for COBS BACnet frames */ /* not enough to do a header */ - if (buffer_len < 8) { + if (buffer_size < 8) { return 0; } - buffer[0] = 0x55; buffer[1] = 0xFF; buffer[2] = frame_type; @@ -229,46 +253,87 @@ uint16_t MSTP_Create_Frame(uint8_t *buffer, /* where frame is loaded */ buffer[6] = data_len & 0xFF; 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] = crc16 & 0xFF; /* LSB first */ - index++; - buffer[index] = crc16 >> 8; - index++; - } else { + + if ((data_len > 501) || ((frame_type >= Nmin_COBS_type) && + (frame_type <= Nmax_COBS_type))) { + /* COBS encoded frame */ + if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) { + frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY; + cobs_bacnet_frame = true; + } else if (frame_type == FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) { + frame_type = FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY; + cobs_bacnet_frame = true; + } else if ((frame_type < Nmin_COBS_type) || + (frame_type > Nmax_COBS_type)) { + /* I'm sorry, Dave, I'm afraid I can't do that. */ return 0; } + cobs_len = cobs_frame_encode(buffer, buffer_size, data, data_len); + /* check the results of COBs encoding for validity */ + if (cobs_bacnet_frame) { + if (cobs_len < Nmin_COBS_length_BACnet) { + return 0; + } else if (cobs_len > Nmax_COBS_length_BACnet) { + return 0; + } + } else { + if (cobs_len < Nmin_COBS_length) { + return 0; + } else if (cobs_len > Nmax_COBS_length) { + return 0; + } + } + /* for COBS, we must subtract two before use as the + MS/TP frame length field since CRC32 is 2 bytes longer + than CRC16 in original MSTP and non-COBS devices need + to be able to ingest the entire frame */ + index = index + cobs_len - 2; + } else if (data_len > 0) { + while (data_len && data && (index < buffer_size)) { + buffer[index] = *data; + crc16 = CRC_Calc_Data(buffer[index], crc16); + data++; + index++; + data_len--; + } + if ((index + 2) > buffer_size) { + return 0; + } + crc16 = ~crc16; + buffer[index] = crc16 & 0xFF; /* LSB first */ + index++; + buffer[index] = crc16 >> 8; + index++; } return index; /* returns the frame length */ } +/** + * @brief Send an MS/TP Frame + * @param mstp_port - port to send from + * @param frame_type - type of frame to send - see defines + * @param destination - destination address + * @param source - source address + * @param data - any data to be sent - may be null + * @param data_len - number of bytes of data (up to 501) + */ void MSTP_Create_And_Send_Frame( - volatile struct mstp_port_struct_t *mstp_port, /* port to send from */ - uint8_t frame_type, /* type of frame to send - see defines */ - uint8_t destination, /* destination address */ - uint8_t source, /* source address */ - uint8_t *data, /* any data to be sent - may be null */ + volatile struct mstp_port_struct_t *mstp_port, + uint8_t frame_type, + uint8_t destination, + uint8_t source, + uint8_t *data, uint16_t data_len) -{ /* number of bytes of data (up to 501) */ +{ uint16_t len = 0; /* number of bytes to send */ len = MSTP_Create_Frame((uint8_t *)&mstp_port->OutputBuffer[0], mstp_port->OutputBufferSize, frame_type, destination, source, data, data_len); - RS485_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], len); + MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], len); /* FIXME: be sure to reset SilenceTimer() after each octet is sent! */ } @@ -533,23 +598,26 @@ void MSTP_Receive_Frame_FSM(volatile struct mstp_port_struct_t *mstp_port) mstp_port->DataCRC = CRC_Calc_Data( mstp_port->DataRegister, mstp_port->DataCRC); mstp_port->DataCRCActualMSB = mstp_port->DataRegister; - mstp_port->InputBuffer[mstp_port->Index] = - mstp_port->DataRegister; + if (mstp_port->Index < mstp_port->InputBufferSize) { + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + } mstp_port->Index++; /* SKIP_DATA or DATA - no change in state */ } else if (mstp_port->Index == (mstp_port->DataLength + 1)) { /* CRC2 */ - mstp_port->InputBuffer[mstp_port->Index] = - mstp_port->DataRegister; + if (mstp_port->Index < mstp_port->InputBufferSize) { + mstp_port->InputBuffer[mstp_port->Index] = + mstp_port->DataRegister; + } mstp_port->DataCRC = CRC_Calc_Data( mstp_port->DataRegister, mstp_port->DataCRC); mstp_port->DataCRCActualLSB = mstp_port->DataRegister; printf_receive_data("%s", mstptext_frame_type((unsigned)mstp_port->FrameType)); - if ((mstp_port->FrameType == - FRAME_TYPE_BACNET_EXTENDED_DATA_EXPECTING_REPLY) || - (mstp_port->FrameType == - FRAME_TYPE_BACNET_EXTENDED_DATA_NOT_EXPECTING_REPLY)) { + if (((mstp_port->Index+1) < mstp_port->InputBufferSize) && + (mstp_port->FrameType >= Nmin_COBS_type) && + (mstp_port->FrameType <= Nmax_COBS_type)) { if (cobs_frame_decode( &mstp_port->InputBuffer[mstp_port->Index + 1], mstp_port->InputBufferSize, @@ -756,7 +824,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) } else { uint8_t frame_type = mstp_port->OutputBuffer[2]; uint8_t destination = mstp_port->OutputBuffer[3]; - RS485_Send_Frame(mstp_port, + MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], (uint16_t)length); mstp_port->FrameCount++; switch (frame_type) { @@ -1135,7 +1203,7 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) /* then call MSTP_Create_And_Send_Frame to transmit the reply * frame */ /* and enter the IDLE state to wait for the next frame. */ - RS485_Send_Frame(mstp_port, + MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], (uint16_t)length); mstp_port->master_state = MSTP_MASTER_STATE_IDLE; /* clear our flag we were holding for comparison */ @@ -1207,7 +1275,7 @@ void MSTP_Slave_Node_FSM(volatile struct mstp_port_struct_t *mstp_port) * reply frame */ /* and enter the IDLE state to wait for the next frame. */ - RS485_Send_Frame(mstp_port, + MSTP_Send_Frame(mstp_port, (uint8_t *)&mstp_port->OutputBuffer[0], (uint16_t)length); /* clear our flag we were holding for comparison */ diff --git a/src/bacnet/datalink/mstp.h b/src/bacnet/datalink/mstp.h index b6874f7b..1d2a9a8a 100644 --- a/src/bacnet/datalink/mstp.h +++ b/src/bacnet/datalink/mstp.h @@ -220,6 +220,7 @@ extern "C" { /* functions used by the MS/TP state machine to put or get data */ /* FIXME: developer must implement these in their DLMSTP module */ + BACNET_STACK_EXPORT uint16_t MSTP_Put_Receive( volatile struct mstp_port_struct_t *mstp_port); @@ -238,6 +239,12 @@ extern "C" { volatile struct mstp_port_struct_t *mstp_port, unsigned timeout); /* milliseconds to wait for a packet */ + BACNET_STACK_EXPORT + void MSTP_Send_Frame( + volatile struct mstp_port_struct_t *mstp_port, + uint8_t * buffer, + uint16_t nbytes); + #ifdef __cplusplus } #endif /* __cplusplus */