From 4f42b205903a7b536e8af5769eaed3778ac70a14 Mon Sep 17 00:00:00 2001 From: skarg Date: Thu, 23 Aug 2007 00:28:14 +0000 Subject: [PATCH] Modified the MS/TP Master Node state machine to handle Data Expecting Reply in a more appropriate manner. Updated the MS/TP monitor rx_fsm, and updated the linux and windows dlsmtp for new functionality. --- bacnet-stack/mstp.c | 72 +++---- bacnet-stack/mstp.h | 10 +- bacnet-stack/npdu.c | 31 +++ bacnet-stack/ports/at91sam7s/dlmstp.c | 230 ++++++++++++++++++++--- bacnet-stack/ports/at91sam7s/makefile | 2 +- bacnet-stack/ports/linux/dlmstp.c | 225 ++++++++++++++++++++-- bacnet-stack/ports/linux/rx_fsm.c | 44 +++-- bacnet-stack/ports/win32/dlmstp.c | 259 +++++++++++++++++++++++--- bacnet-stack/ports/win32/rx_fsm.c | 62 +++--- bacnet-stack/ports/win32/stdbool.h | 2 +- 10 files changed, 782 insertions(+), 155 deletions(-) diff --git a/bacnet-stack/mstp.c b/bacnet-stack/mstp.c index 3cb4c4c8..b65571b7 100644 --- a/bacnet-stack/mstp.c +++ b/bacnet-stack/mstp.c @@ -727,28 +727,25 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) break; } } - mstp_port->ReceivedValidFrame = false; + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (mstp_port->master_state != + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + mstp_port->ReceivedValidFrame = false; + } } break; /* In the USE_TOKEN state, the node is allowed to send one or */ /* more data frames. These may be BACnet Data frames or */ /* proprietary frames. */ case MSTP_MASTER_STATE_USE_TOKEN: - length = MSTP_Get_Send( - mstp_port->This_Station, - (uint8_t *)&mstp_port->OutputBuffer[0], - mstp_port->OutputBufferSize, - 0); /* milliseconds to wait for a packet */ + /* FIXME: We could wait for up to Tusage_delay */ + length = MSTP_Get_Send(mstp_port, 0); if (length < 1) { /* NothingToSend */ mstp_port->FrameCount = mstp_port->Nmax_info_frames; mstp_port->master_state = MSTP_MASTER_STATE_DONE_WITH_TOKEN; transition_now = true; - } else if (mstp_port->SilenceTimer() > Tusage_delay) { - /* Don't send it if we are too late in getting out. */ - /* Don't worry. If we missed our timing deadline, - another token will be sent */ - mstp_port->master_state = MSTP_MASTER_STATE_IDLE; } else { uint8_t frame_type = mstp_port->OutputBuffer[2]; uint8_t destination = mstp_port->OutputBuffer[3]; @@ -1078,14 +1075,9 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* 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 0 - /* FIXME: we always defer the reply to be legal */ - /* FIXME: if we knew the APDU type received, we could - see if the next message was that same APDU type - along with the matching src/dest and invoke ID */ - /* FIXME: we could use 2 queues: one for DER and one for non-DER */ - if ((mstp_port->SilenceTimer() <= Treply_delay) && - mstp_port->TxReady) { + /* FIXME: we could wait for up to Treply_delay */ + length = MSTP_Get_Reply(mstp_port, 0); + if (length > 0) { /* Reply */ /* If a reply is available from the higher layers */ /* within Treply_delay after the reception of the */ @@ -1093,38 +1085,28 @@ bool MSTP_Master_Node_FSM(volatile struct mstp_port_struct_t * mstp_port) /* (the mechanism used to determine this is a local matter), */ /* then call MSTP_Create_And_Send_Frame to transmit the reply frame */ /* and enter the IDLE state to wait for the next frame. */ - length = MSTP_Get_Send( - mstp_port->This_Station, - (uint8_t *)&mstp_port->OutputBuffer[0], - mstp_port->OutputBufferSize, - 0); /* milliseconds to wait for a packet */ - if ((mstp_port->FrameType == - FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY) - && (length > 0)) { - RS485_Send_Frame(mstp_port, - (uint8_t *) & mstp_port->TxBuffer[0], - length); - mstp_port->TxReady = false; - mstp_port->master_state = MSTP_MASTER_STATE_IDLE; - } - } else -#endif - /* DeferredReply */ - /* If no reply will be available from the higher layers */ - /* within Treply_delay after the reception of the */ - /* final octet of the requesting frame (the mechanism */ - /* used to determine this is a local matter), */ - /* then an immediate reply is not possible. */ - /* Any reply shall wait until this node receives the token. */ - /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ - /* and enter the IDLE state. */ - { + RS485_Send_Frame(mstp_port, + (uint8_t *) & mstp_port->OutputBuffer[0], + length); + mstp_port->master_state = MSTP_MASTER_STATE_IDLE; + } else { + /* DeferredReply */ + /* If no reply will be available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame (the mechanism */ + /* used to determine this is a local matter), */ + /* then an immediate reply is not possible. */ + /* Any reply shall wait until this node receives the token. */ + /* Call MSTP_Create_And_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ MSTP_Create_And_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; } + /* clear our flag we were holding for comparison */ + mstp_port->ReceivedValidFrame = false; break; default: mstp_port->master_state = MSTP_MASTER_STATE_IDLE; diff --git a/bacnet-stack/mstp.h b/bacnet-stack/mstp.h index eba8f2f3..cafe9fd2 100644 --- a/bacnet-stack/mstp.h +++ b/bacnet-stack/mstp.h @@ -292,9 +292,13 @@ extern "C" { /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout); /* milliseconds to wait for a packet */ + /* for the MS/TP state machine to use for getting the reply for + Data-Expecting-Reply Frame */ + /* Return: amount of PDU data */ + uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, unsigned timeout); /* milliseconds to wait for a packet */ #ifdef __cplusplus diff --git a/bacnet-stack/npdu.c b/bacnet-stack/npdu.c index 4e3823f8..a71144d5 100644 --- a/bacnet-stack/npdu.c +++ b/bacnet-stack/npdu.c @@ -190,6 +190,37 @@ int npdu_encode_pdu(uint8_t * npdu, /* Configure the NPDU portion of the packet for an APDU */ /* This function does not handle the network messages, just APDUs. */ +/* From BACnet 5.1: +Applicability of ICI parameters for abstract service primitives +Service Primitive DA SA NP DER +----------------- --- --- --- --- +CONF_SERV.request Yes No Yes Yes +CONF_SERV.indication Yes Yes Yes Yes +CONF_SERV.response Yes No Yes Yes +CONF_SERV.confirm Yes Yes Yes No +UNCONF_SERV.request Yes No Yes No +UNCONF_SERV.indication Yes Yes Yes No +REJECT.request Yes No Yes No +REJECT.indication Yes Yes Yes No +SEGMENT_ACK.request Yes No Yes No +SEGMENT_ACK.indication Yes Yes Yes No +ABORT.request Yes No Yes No +ABORT.indication Yes Yes Yes No + +Where: +'destination_address' (DA): the address of the device(s) intended +to receive the service primitive. Its format (device name, +network address, etc.) is a local matter. This address may +also be a multicast, local broadcast or global broadcast type. +'source_address' (SA): the address of the device from which +the service primitive was received. Its format (device name, +network address, etc.) is a local matter. +'network_priority' (NP): a four-level network priority parameter +described in 6.2.2. +'data_expecting_reply' (DER): a Boolean parameter that indicates +whether (TRUE) or not (FALSE) a reply service primitive +is expected for the service being issued. +*/ void npdu_encode_npdu_data(BACNET_NPDU_DATA * npdu_data, bool data_expecting_reply, BACNET_MESSAGE_PRIORITY priority) { diff --git a/bacnet-stack/ports/at91sam7s/dlmstp.c b/bacnet-stack/ports/at91sam7s/dlmstp.c index 227e93ea..6d5babe4 100644 --- a/bacnet-stack/ports/at91sam7s/dlmstp.c +++ b/bacnet-stack/ports/at91sam7s/dlmstp.c @@ -31,6 +31,8 @@ #include "dlmstp.h" #include "rs485.h" #include "npdu.h" +#include "apdu.h" +#include "bits.h" /* This file has been customized for use with the AT91SAM7S-EK */ #include "board.h" #include "timer.h" @@ -196,7 +198,7 @@ void dlmstp_fill_bacnet_address(BACNET_ADDRESS * src, uint8_t mstp_address) /* for the MS/TP state machine to use for putting received data */ uint16_t MSTP_Put_Receive( - volatile struct mstp_port_struct_t *mstp_port) + volatile struct mstp_port_struct_t *mstp_port) { DLMSTP_PACKET packet; uint16_t pdu_len = mstp_port->DataLength; @@ -222,34 +224,216 @@ uint16_t MSTP_Put_Receive( /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ + volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) /* milliseconds to wait for a packet */ { uint16_t pdu_len = 0; /* return value */ uint8_t destination = 0; /* destination address */ - if (Transmit_Packet.ready) { - /* load destination MAC address */ - if (Transmit_Packet.address.mac_len == 1) { - destination = Transmit_Packet.address.mac[0]; - } else { - return 0; - } - if ((8 /* header len */ + Transmit_Packet.pdu_len) > MAX_MPDU) { - return 0; - } - /* convert the PDU into the MSTP Frame */ - pdu_len = MSTP_Create_Frame( - pdu, /* <-- loading this */ - max_pdu, - Transmit_Packet.frame_type, - destination, src, - &Transmit_Packet.pdu[0], - Transmit_Packet.pdu_len); - Transmit_Packet.ready = false; + if (!Transmit_Packet.ready) { + return 0; } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame( + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, + Transmit_Packet.frame_type, + destination, + mstp_port->This_Station, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +bool dlmstp_same_bacnet_address(BACNET_ADDRESS * dest, BACNET_ADDRESS * src) +{ + int i = 0; + + if (!dest || !src) + return false; + if (dest->mac_len != src->mac_len) + return false; + for (i = 0; i < dest->mac_len; i++) { + if (dest->mac[i] != src->mac[i]) + return false; + } + if (dest->net != src->net) + return false; + if (dest->len != src->len) + return false; + for (i = 0; i < dest->len; i++) { + if (dest->adr[i] != src->adr[i]) + return false; + } + + return true; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t *request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t *reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS *dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = npdu_decode(&request_pdu[0], + NULL, &request.address, &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset+2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset+5]; + else + request.service_choice = request_pdu[offset+3]; + /* decode the reply data */ + dlmstp_copy_bacnet_address(&reply.address, dest_address); + offset = npdu_decode(&reply_pdu[0], + &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset+2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+5]; + else + reply.service_choice = reply_pdu[offset+3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset+1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+4]; + else + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset+1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!dlmstp_same_bacnet_address(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* does destination match source? */ + if (mstp_port->SourceAddress != destination) { + return 0; + } + /* is this the reply to the DER? */ + matched = dlmstp_compare_data_expecting_reply( + &mstp_port->InputBuffer[0], + mstp_port->DataLength, + mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (!matched) + return 0; + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame( + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, + Transmit_Packet.frame_type, + destination, + mstp_port->This_Station, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; return pdu_len; } diff --git a/bacnet-stack/ports/at91sam7s/makefile b/bacnet-stack/ports/at91sam7s/makefile index 6d71d92a..c8374268 100644 --- a/bacnet-stack/ports/at91sam7s/makefile +++ b/bacnet-stack/ports/at91sam7s/makefile @@ -13,8 +13,8 @@ SIZE = arm-elf-size LDSCRIPT=at91sam7s256.ld BACNET_FLAGS = -DBACDL_MSTP +BACNET_FLAGS += -DMAX_TSM_TRANSACTIONS=0 BACNET_FLAGS += -DPRINT_ENABLED=0 -BACNET_FLAGS += -DBIG_ENDIAN=0 BACNET_FLAGS += -DMAX_APDU=480 #BACNET_FLAGS += -DDLMSTP_TEST diff --git a/bacnet-stack/ports/linux/dlmstp.c b/bacnet-stack/ports/linux/dlmstp.c index fdf1fe5f..b3b74172 100644 --- a/bacnet-stack/ports/linux/dlmstp.c +++ b/bacnet-stack/ports/linux/dlmstp.c @@ -70,7 +70,7 @@ static void Timer_Silence_Reset(void) SilenceTime = 0; } -void dlmstp_millisecond_timer(void) +static void dlmstp_millisecond_timer(void) { INCREMENT_AND_LIMIT_UINT16(SilenceTime); } @@ -290,15 +290,11 @@ uint16_t MSTP_Put_Receive( /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ -uint16_t MSTP_Get_Send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ +int dlmstp_get_transmit_packet( + DLMSTP_PACKET *packet, unsigned timeout) /* milliseconds to wait for a packet */ { - uint16_t pdu_len = 0; /* return value */ - int received_bytes = 0; - DLMSTP_PACKET packet; + int received_bytes = 0; /* return value */ struct timeval select_timeout; fd_set read_fds; int max = 0; @@ -323,8 +319,8 @@ uint16_t MSTP_Get_Send( if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) { received_bytes = read( Transmit_Client_SockFD, - &packet, - sizeof(packet)); + packet, + sizeof(DLMSTP_PACKET)); } else { return 0; } @@ -344,30 +340,227 @@ uint16_t MSTP_Get_Send( return 0; } - if (received_bytes == 0) - return 0; + return (received_bytes); +} +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + uint16_t pdu_len = 0; /* return value */ + int received_bytes = 0; + DLMSTP_PACKET packet; + uint8_t destination = 0; /* destination address */ + + received_bytes = dlmstp_get_transmit_packet(&packet, timeout); + if (received_bytes <= 0) + return 0; /* load destination MAC address */ if (packet.address.mac_len == 1) { destination = packet.address.mac[0]; } else { return 0; } - if ((8 /* header len */ + packet.pdu_len) > MAX_MPDU) { + if ((MAX_HEADER + packet.pdu_len) > MAX_MPDU) { return 0; } /* convert the PDU into the MSTP Frame */ pdu_len = MSTP_Create_Frame( - pdu, /* <-- loading this */ - max_pdu, + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, packet.frame_type, - destination, src, + destination, + mstp_port->This_Station, &packet.pdu[0], packet.pdu_len); return pdu_len; } +bool dlmstp_same_bacnet_address(BACNET_ADDRESS * dest, BACNET_ADDRESS * src) +{ + int i = 0; + + if (!dest || !src) + return false; + if (dest->mac_len != src->mac_len) + return false; + for (i = 0; i < dest->mac_len; i++) { + if (dest->mac[i] != src->mac[i]) + return false; + } + if (dest->net != src->net) + return false; + if (dest->len != src->len) + return false; + for (i = 0; i < dest->len; i++) { + if (dest->adr[i] != src->adr[i]) + return false; + } + + return true; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t *request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t *reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS *dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = npdu_decode(&request_pdu[0], + NULL, &request.address, &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset+2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset+5]; + else + request.service_choice = request_pdu[offset+3]; + /* decode the reply data */ + dlmstp_copy_bacnet_address(&reply.address, dest_address); + offset = npdu_decode(&reply_pdu[0], + &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset+2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+5]; + else + reply.service_choice = reply_pdu[offset+3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset+1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+4]; + else + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset+1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!dlmstp_same_bacnet_address(&request.address, &reply.address)) { + return false; + } + + return true; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + int received_bytes = 0; + DLMSTP_PACKET packet; + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + received_bytes = dlmstp_get_transmit_packet(&packet, timeout); + if (received_bytes <= 0) + return 0; + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* is this the reply to the DER? */ + matched = dlmstp_compare_data_expecting_reply( + &mstp_port->InputBuffer[0], + mstp_port->DataLength, + mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (matched) { + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame( + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, + Transmit_Packet.frame_type, + destination, mstp_port->This_Station, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + /* not used here, but setting it anyway */ + Transmit_Packet.ready = false; + } else { + /* put it back into the queue */ + (void)write(Transmit_Server_SockFD, &packet, sizeof(packet)); + } + + return pdu_len; +} + void dlmstp_set_mac_address(uint8_t mac_address) { /* Master Nodes can only have address 0-127 */ diff --git a/bacnet-stack/ports/linux/rx_fsm.c b/bacnet-stack/ports/linux/rx_fsm.c index 180d6a28..80141e36 100644 --- a/bacnet-stack/ports/linux/rx_fsm.c +++ b/bacnet-stack/ports/linux/rx_fsm.c @@ -36,6 +36,7 @@ #include #include #include +#include /* Linux includes */ #include @@ -50,13 +51,26 @@ #include "mstp.h" #include "mstptext.h" -#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} - /* local port data - shared with RS-485 */ static volatile struct mstp_port_struct_t MSTP_Port; /* buffers needed by mstp port struct */ static uint8_t RxBuffer[MAX_MPDU]; static uint8_t TxBuffer[MAX_MPDU]; +static uint16_t SilenceTime; +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} +static uint16_t Timer_Silence(void) +{ + return SilenceTime; +} +static void Timer_Silence_Reset(void) +{ + SilenceTime = 0; +} + +static void dlmstp_millisecond_timer(void) +{ + INCREMENT_AND_LIMIT_UINT16(SilenceTime); +} void *milliseconds_task(void *pArg) { @@ -67,17 +81,12 @@ void *milliseconds_task(void *pArg) for (;;) { nanosleep(&timeOut, &remains); - INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); + dlmstp_millisecond_timer(); } return NULL; } -volatile uint16_t *dlmstp_millisecond_timer_address(void) -{ - return (&(MSTP_Port.SilenceTimer)); -} - /* functions used by the MS/TP state machine to put or get data */ uint16_t MSTP_Put_Receive( volatile struct mstp_port_struct_t *mstp_port) @@ -90,14 +99,19 @@ uint16_t MSTP_Put_Receive( /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ + volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) /* milliseconds to wait for a packet */ { - (void)src; - (void)pdu; - (void)max_pdu; + (void)mstp_port; + (void)timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + (void)mstp_port; (void)timeout; return 0; } @@ -192,6 +206,8 @@ int main(int argc, char *argv[]) MSTP_Port.This_Station = my_mac; MSTP_Port.Nmax_info_frames = 1; MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; MSTP_Init(mstp_port); mstp_port->Lurking = true; /* start our MilliSec task */ diff --git a/bacnet-stack/ports/win32/dlmstp.c b/bacnet-stack/ports/win32/dlmstp.c index 451bb275..0a5397f1 100644 --- a/bacnet-stack/ports/win32/dlmstp.c +++ b/bacnet-stack/ports/win32/dlmstp.c @@ -89,6 +89,23 @@ void dlmstp_cleanup(void) } } +void dlmstp_copy_bacnet_address(BACNET_ADDRESS * dest, BACNET_ADDRESS * src) +{ + int i = 0; + + if (dest && src) { + dest->mac_len = src->mac_len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->mac[i] = src->mac[i]; + } + dest->net = src->net; + dest->len = src->len; + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = src->adr[i]; + } + } +} + /* returns number of bytes sent on success, zero on failure */ int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */ BACNET_NPDU_DATA * npdu_data, /* network information */ @@ -97,32 +114,23 @@ int dlmstp_send_pdu(BACNET_ADDRESS * dest, /* destination address */ { /* number of bytes of data */ int bytes_sent = 0; uint8_t destination = 0; /* destination address */ + unsigned i = 0; if (!Transmit_Packet.ready) { - if (npdu_data->data_expecting_reply) - Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; - else + if (npdu_data->data_expecting_reply) { + Transmit_Packet.frame_type = + FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { Transmit_Packet.frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; - - /* load destination MAC address */ - if (dest && dest->mac_len == 1) { - destination = dest->mac[0]; - } else { - return -2; } - if ((8 /* header len */ + pdu_len) > MAX_MPDU) { - return -4; + Transmit_Packet.pdu_len = pdu_len; + for (i = 0; i < pdu_len; i++) { + Transmit_Packet.pdu[i] = pdu[i]; } - Transmit_Packet.pdu_len = MSTP_Create_Frame( - (uint8_t *) & Transmit_Packet.pdu[0], - sizeof(Transmit_Packet.pdu), - Transmit_Packet.frame_type, - destination, - MSTP_Port.This_Station, - pdu, pdu_len); + dlmstp_copy_bacnet_address(&Transmit_Packet.address, dest); + bytes_sent = pdu_len + MAX_HEADER; Transmit_Packet.ready = true; - MSTP_Packets++; } return bytes_sent; @@ -276,24 +284,213 @@ uint16_t MSTP_Put_Receive( /* for the MS/TP state machine to use for getting data to send */ /* Return: amount of PDU data */ uint16_t MSTP_Get_Send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ + volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) /* milliseconds to wait for a packet */ { uint16_t pdu_len = 0; + uint8_t destination = 0; /* destination address */ - (void)src; (void)timeout; - if (Transmit_Packet.ready) { - if (Transmit_Packet.pdu_len <= max_pdu) { - memmove(&pdu[0], - (void *) & Transmit_Packet.pdu[0], - Transmit_Packet.pdu_len); - pdu_len = Transmit_Packet.pdu_len; - } - Transmit_Packet.ready = false; + if (!Transmit_Packet.ready) { + return 0; } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame( + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, + Transmit_Packet.frame_type, + destination, + mstp_port->This_Station, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; + + return pdu_len; +} + +bool dlmstp_same_bacnet_address(BACNET_ADDRESS * dest, BACNET_ADDRESS * src) +{ + int i = 0; + + if (!dest || !src) + return false; + if (dest->mac_len != src->mac_len) + return false; + for (i = 0; i < dest->mac_len; i++) { + if (dest->mac[i] != src->mac[i]) + return false; + } + if (dest->net != src->net) + return false; + if (dest->len != src->len) + return false; + for (i = 0; i < dest->len; i++) { + if (dest->adr[i] != src->adr[i]) + return false; + } + + return true; +} + +bool dlmstp_compare_data_expecting_reply( + uint8_t *request_pdu, + uint16_t request_pdu_len, + uint8_t src_address, + uint8_t *reply_pdu, + uint16_t reply_pdu_len, + BACNET_ADDRESS *dest_address) +{ + uint16_t offset; + /* One way to check the message is to compare NPDU + src, dest, along with the APDU type, invoke id. + Seems a bit overkill */ + struct DER_compare_t { + BACNET_NPDU_DATA npdu_data; + BACNET_ADDRESS address; + uint8_t pdu_type; + uint8_t invoke_id; + uint8_t service_choice; + }; + struct DER_compare_t request; + struct DER_compare_t reply; + + /* decode the request data */ + request.address.mac[0] = src_address; + request.address.mac_len = 1; + offset = npdu_decode(&request_pdu[0], + NULL, &request.address, &request.npdu_data); + if (request.npdu_data.network_layer_message) { + return false; + } + request.pdu_type = request_pdu[offset] & 0xF0; + if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) { + return false; + } + request.invoke_id = request_pdu[offset+2]; + /* segmented message? */ + if (request_pdu[offset] & BIT3) + request.service_choice = request_pdu[offset+5]; + else + request.service_choice = request_pdu[offset+3]; + /* decode the reply data */ + dlmstp_copy_bacnet_address(&reply.address, dest_address); + offset = npdu_decode(&reply_pdu[0], + &reply.address, NULL, &reply.npdu_data); + if (reply.npdu_data.network_layer_message) { + return false; + } + /* reply could be a lot of things: + confirmed, simple ack, abort, reject, error */ + reply.pdu_type = reply_pdu[offset] & 0xF0; + switch (reply.pdu_type) { + case PDU_TYPE_CONFIRMED_SERVICE_REQUEST: + reply.invoke_id = reply_pdu[offset+2]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+5]; + else + reply.service_choice = reply_pdu[offset+3]; + break; + case PDU_TYPE_SIMPLE_ACK: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_COMPLEX_ACK: + reply.invoke_id = reply_pdu[offset+1]; + /* segmented message? */ + if (reply_pdu[offset] & BIT3) + reply.service_choice = reply_pdu[offset+4]; + else + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_ERROR: + reply.invoke_id = reply_pdu[offset+1]; + reply.service_choice = reply_pdu[offset+2]; + break; + case PDU_TYPE_REJECT: + case PDU_TYPE_ABORT: + reply.invoke_id = reply_pdu[offset+1]; + break; + default: + return false; + } + /* these don't have service choice included */ + if ((reply.pdu_type == PDU_TYPE_REJECT) || + (reply.pdu_type == PDU_TYPE_ABORT)) { + if (request.invoke_id != reply.invoke_id) { + return false; + } + } else { + if (request.invoke_id != reply.invoke_id) { + return false; + } + if (request.service_choice != reply.service_choice) { + return false; + } + } + if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) { + return false; + } + if (request.npdu_data.priority != reply.npdu_data.priority) { + return false; + } + if (!dlmstp_same_bacnet_address(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */ +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + uint16_t pdu_len = 0; /* return value */ + uint8_t destination = 0; /* destination address */ + bool matched = false; + + (void)timeout; + if (!Transmit_Packet.ready) { + return 0; + } + /* load destination MAC address */ + if (Transmit_Packet.address.mac_len == 1) { + destination = Transmit_Packet.address.mac[0]; + } else { + return 0; + } + if ((MAX_HEADER + Transmit_Packet.pdu_len) > MAX_MPDU) { + return 0; + } + /* is this the reply to the DER? */ + matched = dlmstp_compare_data_expecting_reply( + &mstp_port->InputBuffer[0], + mstp_port->DataLength, + mstp_port->SourceAddress, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len, + &Transmit_Packet.address); + if (!matched) + return 0; + /* convert the PDU into the MSTP Frame */ + pdu_len = MSTP_Create_Frame( + &mstp_port->OutputBuffer[0], /* <-- loading this */ + mstp_port->OutputBufferSize, + Transmit_Packet.frame_type, + destination, mstp_port->This_Station, + &Transmit_Packet.pdu[0], + Transmit_Packet.pdu_len); + Transmit_Packet.ready = false; return pdu_len; } diff --git a/bacnet-stack/ports/win32/rx_fsm.c b/bacnet-stack/ports/win32/rx_fsm.c index 8382ab29..34ebecb4 100644 --- a/bacnet-stack/ports/win32/rx_fsm.c +++ b/bacnet-stack/ports/win32/rx_fsm.c @@ -36,6 +36,7 @@ #include #include #include +#include /* Windows includes */ #define WIN32_LEAN_AND_MEAN @@ -49,46 +50,63 @@ #include "mstptext.h" #include "crc.h" -#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} - /* local port data - shared with RS-485 */ volatile struct mstp_port_struct_t MSTP_Port; static uint8_t RxBuffer[MAX_MPDU]; static uint8_t TxBuffer[MAX_MPDU]; +static uint16_t SilenceTime; +#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;} +static uint16_t Timer_Silence(void) +{ + return SilenceTime; +} +static void Timer_Silence_Reset(void) +{ + SilenceTime = 0; +} + +static void dlmstp_millisecond_timer(void) +{ + INCREMENT_AND_LIMIT_UINT16(SilenceTime); +} void *milliseconds_task(void *pArg) { + (void)pArg; for (;;) { Sleep(1); dlmstp_millisecond_timer(); } - return NULL; + //return NULL; } -void dlmstp_millisecond_timer(void) +/* functions used by the MS/TP state machine to put or get data */ +uint16_t MSTP_Put_Receive( + volatile struct mstp_port_struct_t *mstp_port) { - INCREMENT_AND_LIMIT_UINT16(MSTP_Port.SilenceTimer); -} - -uint16_t dlmstp_put_receive( - uint8_t src, /* source MS/TP address */ - uint8_t * pdu, /* PDU data */ - uint16_t pdu_len) -{ - (void)src; - (void)pdu; - (void)pdu_len; - + (void)mstp_port; + return 0; } -uint16_t dlmstp_get_send( - uint8_t src, /* source MS/TP address for creating packet */ - uint8_t * pdu, /* data to send */ - uint16_t max_pdu, /* amount of space available */ +/* for the MS/TP state machine to use for getting data to send */ +/* Return: amount of PDU data */ +uint16_t MSTP_Get_Send( + volatile struct mstp_port_struct_t *mstp_port, unsigned timeout) /* milliseconds to wait for a packet */ { + (void)mstp_port; + (void)timeout; + return 0; +} + +uint16_t MSTP_Get_Reply( + volatile struct mstp_port_struct_t *mstp_port, + unsigned timeout) /* milliseconds to wait for a packet */ +{ + (void)mstp_port; + (void)timeout; return 0; } @@ -163,6 +181,8 @@ int main(int argc, char *argv[]) MSTP_Port.This_Station = my_mac; MSTP_Port.Nmax_info_frames = 1; MSTP_Port.Nmax_master = 127; + MSTP_Port.SilenceTimer = Timer_Silence; + MSTP_Port.SilenceTimerReset = Timer_Silence_Reset; MSTP_Init(&MSTP_Port); mstp_port->Lurking = true; /* start our MilliSec task */ @@ -185,6 +205,6 @@ int main(int argc, char *argv[]) } } - return 0; + //return 0; } diff --git a/bacnet-stack/ports/win32/stdbool.h b/bacnet-stack/ports/win32/stdbool.h index 29c310c3..b6bd2e90 100644 --- a/bacnet-stack/ports/win32/stdbool.h +++ b/bacnet-stack/ports/win32/stdbool.h @@ -6,7 +6,7 @@ #if !defined(__cplusplus) -/*typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool;*/ +typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool; #define bool _Bool #define true 1 #define false 0