From 64e9b54dbd96fc7691b5d4ef764550c8b46ef8e7 Mon Sep 17 00:00:00 2001 From: skarg Date: Thu, 6 Dec 2007 22:31:14 +0000 Subject: [PATCH] Adding a DOS port to run on x86 single board computer. --- bacnet-stack/demo/object/lsp.c | 4 +- bacnet-stack/demo/server/main.c | 2 +- bacnet-stack/include/bacenum.h | 50 +- bacnet-stack/include/config.h | 15 + bacnet-stack/ports/dos/dlmstp.c | 1319 +++++++++++++++++++++++++++++ bacnet-stack/ports/dos/extkword.h | 131 +++ bacnet-stack/ports/dos/mk_fp.h | 23 + bacnet-stack/ports/dos/pchwio.c | 70 ++ bacnet-stack/ports/dos/pchwio.h | 65 ++ bacnet-stack/ports/dos/queue.c | 61 ++ bacnet-stack/ports/dos/queue.h | 41 + bacnet-stack/ports/dos/readme.txt | 5 + bacnet-stack/ports/dos/rs485.c | 284 +++++++ bacnet-stack/ports/dos/rs485.h | 60 ++ bacnet-stack/ports/dos/serial.c | 342 ++++++++ bacnet-stack/ports/dos/serial.h | 106 +++ bacnet-stack/ports/dos/stdbool.h | 20 + bacnet-stack/ports/dos/stdint.h | 31 + bacnet-stack/ports/dos/timer.c | 172 ++++ bacnet-stack/ports/dos/timer.h | 47 + bacnet-stack/src/apdu.c | 18 +- bacnet-stack/src/bactext.c | 10 +- bacnet-stack/src/rpm.c | 8 +- 23 files changed, 2838 insertions(+), 46 deletions(-) create mode 100644 bacnet-stack/ports/dos/dlmstp.c create mode 100644 bacnet-stack/ports/dos/extkword.h create mode 100644 bacnet-stack/ports/dos/mk_fp.h create mode 100644 bacnet-stack/ports/dos/pchwio.c create mode 100644 bacnet-stack/ports/dos/pchwio.h create mode 100644 bacnet-stack/ports/dos/queue.c create mode 100644 bacnet-stack/ports/dos/queue.h create mode 100644 bacnet-stack/ports/dos/readme.txt create mode 100644 bacnet-stack/ports/dos/rs485.c create mode 100644 bacnet-stack/ports/dos/rs485.h create mode 100644 bacnet-stack/ports/dos/serial.c create mode 100644 bacnet-stack/ports/dos/serial.h create mode 100644 bacnet-stack/ports/dos/stdbool.h create mode 100644 bacnet-stack/ports/dos/stdint.h create mode 100644 bacnet-stack/ports/dos/timer.c create mode 100644 bacnet-stack/ports/dos/timer.h diff --git a/bacnet-stack/demo/object/lsp.c b/bacnet-stack/demo/object/lsp.c index 3ccf042e..1e288fca 100644 --- a/bacnet-stack/demo/object/lsp.c +++ b/bacnet-stack/demo/object/lsp.c @@ -104,7 +104,7 @@ void Life_Safety_Point_Init( Life_Safety_Point_Mode[i] = LIFE_SAFETY_MODE_DEFAULT; Life_Safety_Point_State[i] = LIFE_SAFETY_STATE_QUIET; Life_Safety_Point_Silenced_State[i] = SILENCED_STATE_UNSILENCED; - Life_Safety_Point_Operation[i] = LIFE_SAFETY_OPERATION_NONE; + Life_Safety_Point_Operation[i] = LIFE_SAFETY_OP_NONE; } } @@ -202,7 +202,7 @@ int Life_Safety_Point_Encode_Property_APDU( BACNET_LIFE_SAFETY_STATE present_value = LIFE_SAFETY_STATE_QUIET; BACNET_LIFE_SAFETY_MODE mode = LIFE_SAFETY_MODE_DEFAULT; BACNET_SILENCED_STATE silenced_state = SILENCED_STATE_UNSILENCED; - BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OPERATION_NONE; + BACNET_LIFE_SAFETY_OPERATION operation = LIFE_SAFETY_OP_NONE; unsigned object_index = 0; bool state = false; BACNET_RELIABILITY reliability = RELIABILITY_NO_FAULT_DETECTED; diff --git a/bacnet-stack/demo/server/main.c b/bacnet-stack/demo/server/main.c index 08aecf9b..d60d735f 100644 --- a/bacnet-stack/demo/server/main.c +++ b/bacnet-stack/demo/server/main.c @@ -69,7 +69,7 @@ static void Init_Service_Handlers( /* We must implement read property - it's required! */ apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, handler_read_property); - apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE, + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROP_MULTIPLE, handler_read_property_multiple); apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY, handler_write_property); diff --git a/bacnet-stack/include/bacenum.h b/bacnet-stack/include/bacenum.h index f853e805..11b2f5b7 100644 --- a/bacnet-stack/include/bacenum.h +++ b/bacnet-stack/include/bacenum.h @@ -516,8 +516,8 @@ typedef enum { UNITS_DEGREES_FAHRENHEIT_PER_MINUTE = 94, UNITS_JOULE_SECONDS = 183, UNITS_KILOGRAMS_PER_CUBIC_METER = 186, - UNITS_KILOWATT_HOURS_PER_SQUARE_METER = 137, - UNITS_KILOWATT_HOURS_PER_SQUARE_FOOT = 138, + UNITS_KW_HOURS_PER_SQUARE_METER = 137, + UNITS_KW_HOURS_PER_SQUARE_FOOT = 138, UNITS_MEGAJOULES_PER_SQUARE_METER = 139, UNITS_MEGAJOULES_PER_SQUARE_FOOT = 140, UNITS_NO_UNITS = 95, @@ -649,19 +649,19 @@ typedef enum { } BACNET_LIFE_SAFETY_MODE; typedef enum { - LIFE_SAFETY_OPERATION_NONE = 0, - LIFE_SAFETY_OPERATION_SILENCE = 1, - LIFE_SAFETY_OPERATION_SILENCE_AUDIBLE = 2, - LIFE_SAFETY_OPERATION_SILENCE_VISUAL = 3, - LIFE_SAFETY_OPERATION_RESET = 4, - LIFE_SAFETY_OPERATION_RESET_ALARM = 5, - LIFE_SAFETY_OPERATION_RESET_FAULT = 6, - LIFE_SAFETY_OPERATION_UNSILENCE = 7, - LIFE_SAFETY_OPERATION_UNSILENCE_AUDIBLE = 8, - LIFE_SAFETY_OPERATION_UNSILENCE_VISUAL = 9 - /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ - /* Enumerated values 64-65535 may be used by others subject to */ - /* procedures and constraints described in Clause 23. */ + LIFE_SAFETY_OP_NONE = 0, + LIFE_SAFETY_OP_SILENCE = 1, + LIFE_SAFETY_OP_SILENCE_AUDIBLE = 2, + LIFE_SAFETY_OP_SILENCE_VISUAL = 3, + LIFE_SAFETY_OP_RESET = 4, + LIFE_SAFETY_OP_RESET_ALARM = 5, + LIFE_SAFETY_OP_RESET_FAULT = 6, + LIFE_SAFETY_OP_UNSILENCE = 7, + LIFE_SAFETY_OP_UNSILENCE_AUDIBLE = 8, + LIFE_SAFETY_OP_UNSILENCE_VISUAL = 9 + /* Enumerated values 0-63 are reserved for definition by ASHRAE. */ + /* Enumerated values 64-65535 may be used by others subject to */ + /* procedures and constraints described in Clause 23. */ } BACNET_LIFE_SAFETY_OPERATION; typedef enum { @@ -845,11 +845,11 @@ typedef enum { SERVICE_CONFIRMED_CREATE_OBJECT = 10, SERVICE_CONFIRMED_DELETE_OBJECT = 11, SERVICE_CONFIRMED_READ_PROPERTY = 12, - SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL = 13, - SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE = 14, + SERVICE_CONFIRMED_READ_PROP_CONDITIONAL = 13, + SERVICE_CONFIRMED_READ_PROP_MULTIPLE = 14, SERVICE_CONFIRMED_READ_RANGE = 26, SERVICE_CONFIRMED_WRITE_PROPERTY = 15, - SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE = 16, + SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE = 16, /* Remote Device Management Services */ SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL = 17, SERVICE_CONFIRMED_PRIVATE_TRANSFER = 18, @@ -910,11 +910,11 @@ typedef enum { SERVICE_SUPPORTED_CREATE_OBJECT = 10, SERVICE_SUPPORTED_DELETE_OBJECT = 11, SERVICE_SUPPORTED_READ_PROPERTY = 12, - SERVICE_SUPPORTED_READ_PROPERTY_CONDITIONAL = 13, - SERVICE_SUPPORTED_READ_PROPERTY_MULTIPLE = 14, + SERVICE_SUPPORTED_READ_PROP_CONDITIONAL = 13, + SERVICE_SUPPORTED_READ_PROP_MULTIPLE = 14, SERVICE_SUPPORTED_READ_RANGE = 35, SERVICE_SUPPORTED_WRITE_PROPERTY = 15, - SERVICE_SUPPORTED_WRITE_PROPERTY_MULTIPLE = 16, + SERVICE_SUPPORTED_WRITE_PROP_MULTIPLE = 16, /* Remote Device Management Services */ SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL = 17, SERVICE_SUPPORTED_PRIVATE_TRANSFER = 18, @@ -946,8 +946,8 @@ typedef enum { typedef enum { BVLC_RESULT = 0, BVLC_WRITE_BROADCAST_DISTRIBUTION_TABLE = 1, - BVLC_READ_BROADCAST_DISTRIBUTION_TABLE = 2, - BVLC_READ_BROADCAST_DISTRIBUTION_TABLE_ACK = 3, + BVLC_READ_BROADCAST_DIST_TABLE = 2, + BVLC_READ_BROADCAST_DIST_TABLE_ACK = 3, BVLC_FORWARDED_NPDU = 4, BVLC_REGISTER_FOREIGN_DEVICE = 5, BVLC_READ_FOREIGN_DEVICE_TABLE = 6, @@ -1031,8 +1031,8 @@ typedef enum { NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK = 3, NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK = 4, NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK = 5, - NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE = 6, - NETWORK_MESSAGE_INITIALIZE_ROUTING_TABLE_ACK = 7, + NETWORK_MESSAGE_INIT_RT_TABLE = 6, + NETWORK_MESSAGE_INIT_RT_TABLE_ACK = 7, NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK = 8, NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK = 9, /* X'0A' to X'7F': Reserved for use by ASHRAE, */ diff --git a/bacnet-stack/include/config.h b/bacnet-stack/include/config.h index 1510b317..d4360f2d 100644 --- a/bacnet-stack/include/config.h +++ b/bacnet-stack/include/config.h @@ -67,6 +67,21 @@ /* BACAPP decodes WriteProperty service requests Choose the datatypes that your application supports */ +#if !(defined(BACAPP_NULL) || \ + defined(BACAPP_BOOLEAN) || \ + defined(BACAPP_UNSIGNED) || \ + defined(BACAPP_SIGNED) || \ + defined(BACAPP_REAL) || \ + defined(BACAPP_OCTET_STRING) || \ + defined(BACAPP_CHARACTER_STRING) || \ + defined(BACAPP_BIT_STRING) || \ + defined(BACAPP_ENUMERATED) || \ + defined(BACAPP_DATE) || \ + defined(BACAPP_TIME) || \ + defined(BACAPP_OBJECT_ID)) +#define BACAPP_ALL +#endif + #if defined (BACAPP_ALL) #define BACAPP_NULL #define BACAPP_BOOLEAN diff --git a/bacnet-stack/ports/dos/dlmstp.c b/bacnet-stack/ports/dos/dlmstp.c new file mode 100644 index 00000000..cdaa38d5 --- /dev/null +++ b/bacnet-stack/ports/dos/dlmstp.c @@ -0,0 +1,1319 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2007 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 +#include +#include "bacdef.h" +#include "dlmstp.h" +#include "rs485.h" +#include "crc.h" +#include "npdu.h" +#include "bits.h" +#include "bacaddr.h" + +/* This file has been customized for use with small microprocessors */ +/* Assumptions: + Only one MS/TP datalink layer +*/ +#include "timer.h" + +/* The value 255 is used to denote broadcast when used as a */ +/* destination address but is not allowed as a value for a station. */ +/* Station addresses for master nodes can be 0-127. */ +/* Station addresses for slave nodes can be 127-254. */ +#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 = 0, + MSTP_RECEIVE_STATE_PREAMBLE = 1, + MSTP_RECEIVE_STATE_HEADER = 2, + MSTP_RECEIVE_STATE_DATA = 3 +} MSTP_RECEIVE_STATE; + +/* master node FSM states */ +typedef enum { + MSTP_MASTER_STATE_INITIALIZE = 0, + MSTP_MASTER_STATE_IDLE = 1, + MSTP_MASTER_STATE_USE_TOKEN = 2, + MSTP_MASTER_STATE_WAIT_FOR_REPLY = 3, + MSTP_MASTER_STATE_DONE_WITH_TOKEN = 4, + MSTP_MASTER_STATE_PASS_TOKEN = 5, + MSTP_MASTER_STATE_NO_TOKEN = 6, + MSTP_MASTER_STATE_POLL_FOR_MASTER = 7, + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST = 8 +} MSTP_MASTER_STATE; + +/* The state of the Receive State Machine */ +static MSTP_RECEIVE_STATE Receive_State; +/* When a master node is powered up or reset, */ +/* it shall unconditionally enter the INITIALIZE state. */ +static MSTP_MASTER_STATE Master_State; +/* bit-sized boolean flags */ +static struct mstp_flag_t { + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if an invalid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedInvalidFrame:1; + /* A Boolean flag set to TRUE by the Receive State Machine */ + /* if a valid frame is received. */ + /* Set to FALSE by the main state machine. */ + unsigned ReceivedValidFrame:1; + /* A Boolean flag set to TRUE by the master machine if this node is the */ + /* only known master node. */ + unsigned SoleMaster:1; + /* A Boolean flag set TRUE by the datalink transmit if a + frame is pending */ + unsigned TransmitPacketPending:1; + /* A Boolean flag set TRUE by the datalink transmit if a + pending packet is DataExpectingReply */ + unsigned TransmitPacketDER:1; + /* A Boolean flag set TRUE by the datalink if a + packet has been received, but not processed. */ + unsigned ReceivePacketPending:1; +} MSTP_Flag; + +/* Used to store the data length of a received frame. */ +static uint16_t DataLength; +/* Used to store the destination address of a received frame. */ +static uint8_t DestinationAddress; +/* Used to count the number of received octets or errors. */ +/* This is used in the detection of link activity. */ +/* Compared to Nmin_octets */ +static uint8_t EventCount; +/* Used to store the frame type of a received frame. */ +static uint8_t FrameType; +/* An array of octets, used to store octets as they are received. */ +/* InputBuffer is indexed from 0 to InputBufferSize-1. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *InputBuffer; +static uint8_t InputBufferSize; +/* Used to store the Source Address of a received frame. */ +static uint8_t SourceAddress; +/* "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. */ +static 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. */ +static uint8_t Nmax_info_frames; +/* This parameter represents the value of the Max_Master property of the */ +/* node's Device object. The value of Max_Master specifies the highest */ +/* allowable address for master nodes. The value of Max_Master shall be */ +/* less than or equal to 127. If Max_Master is not writable in a node, */ +/* its value shall be 127. */ +static uint8_t Nmax_master; +/* An array of octets, used to store octets for transmitting */ +/* OutputBuffer is indexed from 0 to OutputBufferSize-1. */ +/* The MAX_PDU size of a frame is MAX_APDU + MAX_NPDU octets. */ +/* FIXME: assign this to an actual array of bytes! */ +/* Note: the buffer is designed as a pointer since some compilers + and microcontroller architectures have limits as to places to + hold contiguous memory. */ +static uint8_t *TransmitPacket; +static uint16_t TransmitPacketLen; +static uint8_t TransmitPacketDest; + +/* The time without a DataAvailable or ReceiveError event before declaration */ +/* of loss of token: 500 milliseconds. */ +#define Tno_token 500 + +/* 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.) */ +#define Treply_timeout 260 + +/* 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.) */ +#define Tusage_timeout 25 + +/* The number of tokens received or used before a Poll For Master cycle */ +/* is executed: 50. */ +#define Npoll 50 + +/* The number of retries on sending Token: 1. */ +#define 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. */ +#define Nmin_octets 4 + +/* The minimum time without a DataAvailable or ReceiveError event within */ +/* a frame before a receiving node may discard the frame: 60 bit times. */ +/* (Implementations may use larger values for this timeout, */ +/* not to exceed 100 milliseconds.) */ +/* At 9600 baud, 60 bit times would be about 6.25 milliseconds */ +/* const uint16_t Tframe_abort = 1 + ((1000 * 60) / 9600); */ +#define Tframe_abort 30 + +/* The maximum idle time a sending node may allow to elapse between octets */ +/* of a frame the node is transmitting: 20 bit times. */ +#define Tframe_gap 20 + +/* 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. */ +#define 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. */ +#define Treply_delay 250 + +/* The width of the time slot within which a node may generate a token: */ +/* 10 milliseconds. */ +#define 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. */ +#define Tusage_delay 15 + +/* we need to be able to increment without rolling over */ +#define INCREMENT_AND_LIMIT_UINT8(x) {if (x < 0xFF) x++;} + +bool dlmstp_init( + char *ifname) +{ + (void) ifname; + /* initialize hardware */ + RS485_Initialize(); + + return true; +} + +void dlmstp_cleanup( + void) +{ + /* nothing to do for static buffers */ +} + +void dlmstp_fill_bacnet_address( + BACNET_ADDRESS * src, + uint8_t mstp_address) +{ + int i = 0; + + if (mstp_address == MSTP_BROADCAST_ADDRESS) { + /* mac_len = 0 if broadcast address */ + src->mac_len = 0; + src->mac[0] = 0; + } else { + src->mac_len = 1; + src->mac[0] = mstp_address; + } + /* fill with 0's starting with index 1; index 0 filled above */ + for (i = 1; i < MAX_MAC_LEN; i++) { + src->mac[i] = 0; + } + src->net = 0; + src->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + src->adr[i] = 0; + } +} + +static 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, + uint8_t 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 */ + reply.address.mac[0] = dest_address; + reply.address.mac_len = 1; + 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; + } + if (request.invoke_id != reply.invoke_id) { + return false; + } + /* these services don't have service choice included */ + if ((reply.pdu_type != PDU_TYPE_REJECT) && + (reply.pdu_type != PDU_TYPE_ABORT)) { + 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 (!bacnet_address_same(&request.address, &reply.address)) { + return false; + } + + return true; +} + +/* 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' */ +static void MSTP_Send_Frame( + uint8_t frame_type, /* type of frame to send - see defines */ + uint8_t destination, /* destination address */ + uint8_t source, /* source address */ + uint8_t * data, /* any data to be sent - may be null */ + uint16_t data_len) +{ /* number of bytes of data (up to 501) */ + uint8_t crc8 = 0xFF; /* used to calculate the crc value */ + uint16_t crc16 = 0xFFFF; /* used to calculate the crc value */ + uint8_t buffer[8]; /* stores the header and data crc */ + uint16_t i = 0; /* used to calculate CRC for data */ + + /* create the MS/TP header */ + 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; + RS485_Turnaround_Delay(); + RS485_Transmitter_Enable(true); + RS485_Send_Data(buffer, 8); + /* send any data */ + if (data_len) { + /* calculate CRC for any data */ + for (i = 0; i < data_len; i++) { + crc16 = CRC_Calc_Data(data[i], crc16); + } + crc16 = ~crc16; + buffer[0] = (crc16 & 0x00FF); + buffer[1] = ((crc16 & 0xFF00) >> 8); + RS485_Send_Data(data, data_len); + RS485_Send_Data(buffer, 2); + } + RS485_Transmitter_Enable(false); +} + +static void MSTP_Receive_Frame_FSM( + void) +{ + /* stores the latest received data octet */ + uint8_t DataRegister = 0; + /* Used to accumulate the CRC on the data field of a frame. */ + static uint16_t DataCRC = 0; + /* Used to accumulate the CRC on the header of a frame. */ + static uint8_t HeaderCRC = 0; + /* Used as an index by the Receive State Machine, + up to a maximum value of the MPDU */ + static uint8_t Index = 0; + + switch (Receive_State) { + case MSTP_RECEIVE_STATE_IDLE: + /* In the IDLE state, the node waits for the beginning of a frame. */ + if (RS485_ReceiveError()) { + /* EatAnError */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0x55) { + /* Preamble1 */ + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } + } + break; + case MSTP_RECEIVE_STATE_PREAMBLE: + /* In the PREAMBLE state, the node waits for the + second octet of the preamble. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* a correct preamble has not been received */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (DataRegister == 0xFF) { + /* Preamble2 */ + Index = 0; + HeaderCRC = 0xFF; + /* receive the remainder of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_HEADER; + } else if (DataRegister == 0x55) { + /* ignore RepeatedPreamble1 */ + /* wait for the second preamble octet. */ + Receive_State = MSTP_RECEIVE_STATE_PREAMBLE; + } else { + /* NotPreamble */ + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_HEADER: + /* In the HEADER state, the node waits for the fixed message header. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index == 0) { + /* FrameType */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + FrameType = DataRegister; + Index = 1; + } else if (Index == 1) { + /* Destination */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DestinationAddress = DataRegister; + Index = 2; + } else if (Index == 2) { + /* Source */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + SourceAddress = DataRegister; + Index = 3; + } else if (Index == 3) { + /* Length1 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength = DataRegister * 256; + Index = 4; + } else if (Index == 4) { + /* Length2 */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + DataLength += DataRegister; + Index = 5; + } else if (Index == 5) { + /* HeaderCRC */ + HeaderCRC = CRC_Calc_Header(DataRegister, HeaderCRC); + /* In the HEADER_CRC state, the node validates the CRC + on the fixed message header. */ + if (HeaderCRC != 0x55) { + /* BadCRC */ + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else { + /* Note: proposed change to BACnet MSTP state machine! + If we don't decode data that is not for us, we could + get confused about the start if the Preamble 55 FF + is part of the data. */ + if ((DataLength) && (DataLength <= InputBufferSize)) { + /* Data */ + Index = 0; + DataCRC = 0xFFFF; + /* receive the data portion of the frame. */ + Receive_State = MSTP_RECEIVE_STATE_DATA; + } else { + if (DataLength == 0) { + /* NoData */ + if ((DestinationAddress == This_Station) || + (DestinationAddress == + MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with + no data has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } else { + /* NotForUs - drop */ + } + } else { + /* FrameTooLong */ + /* indicate that a frame with an illegal or */ + /* unacceptable data length has been received */ + MSTP_Flag.ReceivedInvalidFrame = true; + } + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + } else { + /* indicate that an error has occurred during */ + /* the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of a frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + case MSTP_RECEIVE_STATE_DATA: + /* In the DATA state, the node waits for the data portion of a frame. */ + if (Timer_Silence() > Tframe_abort) { + /* Timeout */ + /* indicate that an error has occurred during the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_ReceiveError()) { + /* Error */ + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + /* indicate that an error has occurred during + the reception of a frame */ + MSTP_Flag.ReceivedInvalidFrame = true; + /* wait for the start of the next frame. */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } else if (RS485_DataAvailable(&DataRegister)) { + Timer_Silence_Reset(); + INCREMENT_AND_LIMIT_UINT8(EventCount); + if (Index < DataLength) { + /* DataOctet */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + InputBuffer[Index] = DataRegister; + Index++; + } else if (Index == DataLength) { + /* CRC1 */ + DataCRC = CRC_Calc_Data(DataRegister, DataCRC); + 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 ((DestinationAddress == This_Station) || + (DestinationAddress == MSTP_BROADCAST_ADDRESS)) { + /* ForUs */ + /* indicate that a frame with no data + has been received */ + MSTP_Flag.ReceivedValidFrame = true; + } + } else { + MSTP_Flag.ReceivedInvalidFrame = true; + } + Receive_State = MSTP_RECEIVE_STATE_IDLE; + } + } + break; + default: + /* shouldn't get here - but if we do... */ + Receive_State = MSTP_RECEIVE_STATE_IDLE; + break; + } + + return; +} + +/* returns true if we need to transition immediately */ +static bool MSTP_Master_Node_FSM( + void) +{ + /* 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. */ + static uint8_t FrameCount; + /* "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. */ + static 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. */ + static uint8_t Poll_Station; + /* A counter of transmission retries used for Token and Poll For Master */ + /* transmission. */ + static unsigned RetryCount; + /* 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. */ + static unsigned TokenCount; + /* next-x-station calculations */ + uint8_t next_poll_station = 0; + uint8_t next_this_station = 0; + uint8_t next_next_station = 0; + /* timeout values */ + uint16_t my_timeout = 10, ns_timeout = 0; + bool matched; + /* transition immediately to the next state */ + bool transition_now = false; + + /* some calculations that several states need */ + next_poll_station = (Poll_Station + 1) % (Nmax_master + 1); + next_this_station = (This_Station + 1) % (Nmax_master + 1); + next_next_station = (Next_Station + 1) % (Nmax_master + 1); + switch (Master_State) { + case MSTP_MASTER_STATE_INITIALIZE: + /* DoneInitializing */ + /* indicate that the next station is unknown */ + Next_Station = This_Station; + Poll_Station = This_Station; + /* cause a Poll For Master to be sent when this node first */ + /* receives the token */ + TokenCount = Npoll; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + break; + case MSTP_MASTER_STATE_IDLE: + /* In the IDLE state, the node waits for a frame. */ + if (Timer_Silence() >= Tno_token) { + /* LostToken */ + /* assume that the token has been lost */ + EventCount = 0; /* Addendum 135-2004d-8 */ + Master_State = MSTP_MASTER_STATE_NO_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* ReceivedInvalidFrame */ + /* invalid frame was received */ + MSTP_Flag.ReceivedInvalidFrame = false; + /* wait for the next frame - remain in IDLE */ + } else if (MSTP_Flag.ReceivedValidFrame == true) { + switch (FrameType) { + case FRAME_TYPE_TOKEN: + /* ReceivedToken */ + /* tokens can't be broadcast */ + if (DestinationAddress == MSTP_BROADCAST_ADDRESS) + break; + MSTP_Flag.ReceivedValidFrame = false; + FrameCount = 0; + MSTP_Flag.SoleMaster = false; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + break; + case FRAME_TYPE_POLL_FOR_MASTER: + /* ReceivedPFM */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + break; + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + /* broadcast DER just remains IDLE */ + if (DestinationAddress != MSTP_BROADCAST_ADDRESS) { + Master_State = + MSTP_MASTER_STATE_ANSWER_DATA_REQUEST; + } + break; + case FRAME_TYPE_TEST_REQUEST: + MSTP_Send_Frame(FRAME_TYPE_TEST_RESPONSE, + SourceAddress, This_Station, NULL, 0); + break; + case FRAME_TYPE_TEST_RESPONSE: + default: + break; + } + /* For DATA_EXPECTING_REPLY, we will keep the Rx Frame for + reference, and the flag will be cleared in the next state */ + if (Master_State != MSTP_MASTER_STATE_ANSWER_DATA_REQUEST) { + MSTP_Flag.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: + /* Note: We could wait for up to Tusage_delay */ + if (!MSTP_Flag.TransmitPacketPending) { + /* NothingToSend */ + FrameCount = Nmax_info_frames; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else { + uint8_t frame_type; + if (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + FrameCount++; + switch (frame_type) { + case FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY: + /* SendAndWait */ + if (TransmitPacketDest == MSTP_BROADCAST_ADDRESS) + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + else + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_REQUEST: + Master_State = MSTP_MASTER_STATE_WAIT_FOR_REPLY; + break; + case FRAME_TYPE_TEST_RESPONSE: + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + default: + /* SendNoWait */ + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + } + } + break; + case MSTP_MASTER_STATE_WAIT_FOR_REPLY: + /* In the WAIT_FOR_REPLY state, the node waits for */ + /* a reply from another node. */ + if (Timer_Silence() >= Treply_timeout) { + /* ReplyTimeout */ + /* assume that the request has failed */ + FrameCount = Nmax_info_frames; + 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.) */ + transition_now = true; + } else { + if (MSTP_Flag.ReceivedInvalidFrame == true) { + /* InvalidFrame */ + /* error in frame reception */ + MSTP_Flag.ReceivedInvalidFrame = false; + Master_State = MSTP_MASTER_STATE_DONE_WITH_TOKEN; + transition_now = true; + } else if (MSTP_Flag.ReceivedValidFrame == true) { + if (DestinationAddress == This_Station) { + /* What did we receive? */ + switch (FrameType) { + case FRAME_TYPE_REPLY_POSTPONED: + /* ReceivedReplyPostponed */ + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + case FRAME_TYPE_TEST_RESPONSE: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + case FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY: + /* ReceivedReply */ + /* or a proprietary type that indicates a reply */ + /* indicate successful reception to the higher layers */ + MSTP_Flag.ReceivePacketPending = true; + Master_State = + MSTP_MASTER_STATE_DONE_WITH_TOKEN; + break; + default: + /* if proprietary frame was expected, you might + need to transition to DONE WITH TOKEN */ + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + } else { + /* ReceivedUnexpectedFrame */ + /* an unexpected frame was received */ + /* This may indicate the presence of multiple tokens */ + /* or a device that didn't see activity after passing */ + /* a token (how lame!). */ + /* Synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + } + MSTP_Flag.ReceivedValidFrame = false; + transition_now = true; + } + } + 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 (FrameCount < Nmax_info_frames) { + /* then this node may send another information frame */ + /* before passing the token. */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + /* Npoll changed in Errata SSPC-135-2004 */ + else if (TokenCount < (Npoll - 1)) { + if ((MSTP_Flag.SoleMaster == true) && + (Next_Station != next_this_station)) { + /* SoleMaster */ + /* there are no other known master nodes to */ + /* which the token may be sent (true master-slave operation). */ + FrameCount = 0; + TokenCount++; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + /* SendToken */ + /* Npoll changed in Errata SSPC-135-2004 */ + /* 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. */ + TokenCount++; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else if (next_poll_station == Next_Station) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMasterRestartMaintenancePFM */ + Poll_Station = next_next_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } else { + /* ResetMaintenancePFM */ + Poll_Station = This_Station; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + TokenCount = 1; /* changed in Errata SSPC-135-2004 */ + EventCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } + } else { + /* SendMaintenancePFM */ + Poll_Station = next_poll_station; + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + break; + /* 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: + if (Timer_Silence() <= Tusage_timeout) { + if (EventCount > Nmin_octets) { + /* SawTokenUser */ + /* Assume that a frame has been sent by the new token user. */ + /* Enter the IDLE state to process the frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + if (RetryCount < Nretry_token) { + /* RetrySendToken */ + RetryCount++; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + EventCount = 0; + /* re-enter the current state to listen for NS */ + /* to begin using the token. */ + } else { + /* FindNewSuccessor */ + /* Assume that NS has failed. */ + Poll_Station = next_next_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* no known successor node */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed in Addendum 135-2004d-8 */ + /* find a new successor to TS */ + Master_State = MSTP_MASTER_STATE_POLL_FOR_MASTER; + } + } + break; + /* The NO_TOKEN state is entered if Timer_Silence() 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: + my_timeout = Tno_token + (Tslot * This_Station); + if (Timer_Silence() < my_timeout) { + if (EventCount > Nmin_octets) { + /* SawFrame */ + /* Some other node exists at a lower address. */ + /* Enter the IDLE state to receive and process the incoming frame. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + } else { + ns_timeout = Tno_token + (Tslot * (This_Station + 1)); + if (Timer_Silence() < ns_timeout) { + /* GenerateToken */ + /* Assume that this node is the lowest numerical address */ + /* on the network and is empowered to create a token. */ + Poll_Station = next_this_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, Poll_Station, + This_Station, NULL, 0); + /* indicate that the next station is unknown */ + Next_Station = This_Station; + RetryCount = 0; + TokenCount = 0; + /* EventCount = 0; removed Addendum 135-2004d-8 */ + /* enter the POLL_FOR_MASTER state to find a new successor to TS. */ + 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: + if (MSTP_Flag.ReceivedValidFrame == true) { + if ((DestinationAddress == This_Station) + && (FrameType == FRAME_TYPE_REPLY_TO_POLL_FOR_MASTER)) { + /* ReceivedReplyToPFM */ + MSTP_Flag.SoleMaster = false; + Next_Station = SourceAddress; + EventCount = 0; + /* Transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + Poll_Station = This_Station; + TokenCount = 0; + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + /* ReceivedUnexpectedFrame */ + /* An unexpected frame was received. */ + /* This may indicate the presence of multiple tokens. */ + /* enter the IDLE state to synchronize with the network. */ + /* This action drops the token. */ + Master_State = MSTP_MASTER_STATE_IDLE; + transition_now = true; + } + MSTP_Flag.ReceivedValidFrame = false; + } else if ((Timer_Silence() > Tusage_timeout) || + (MSTP_Flag.ReceivedInvalidFrame == true)) { + if (MSTP_Flag.SoleMaster == true) { + /* SoleMaster */ + /* There was no valid reply to the periodic poll */ + /* by the sole known master for other masters. */ + FrameCount = 0; + /* TokenCount++; removed in 2004 */ + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } else { + if (Next_Station != This_Station) { + /* DoneWithPFM */ + /* There was no valid reply to the maintenance */ + /* poll for a master at address PS. */ + EventCount = 0; + /* transmit a Token frame to NS */ + MSTP_Send_Frame(FRAME_TYPE_TOKEN, Next_Station, + This_Station, NULL, 0); + RetryCount = 0; + Master_State = MSTP_MASTER_STATE_PASS_TOKEN; + } else { + if (next_poll_station != This_Station) { + /* SendNextPFM */ + Poll_Station = next_poll_station; + /* Transmit a Poll For Master frame to PS. */ + MSTP_Send_Frame(FRAME_TYPE_POLL_FOR_MASTER, + Poll_Station, This_Station, NULL, 0); + RetryCount = 0; + /* Re-enter the current state. */ + } else { + /* DeclareSoleMaster */ + /* to indicate that this station is the only master */ + MSTP_Flag.SoleMaster = true; + FrameCount = 0; + Master_State = MSTP_MASTER_STATE_USE_TOKEN; + transition_now = true; + } + } + } + MSTP_Flag.ReceivedInvalidFrame = false; + } + 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: + /* Note: we could wait for up to Treply_delay */ + if (MSTP_Flag.TransmitPacketPending) { + matched = + dlmstp_compare_data_expecting_reply(&InputBuffer[0], + DataLength, SourceAddress, &TransmitPacket[0], + TransmitPacketLen, TransmitPacketDest); + } + if (MSTP_Flag.TransmitPacketPending && matched) { + /* Reply */ + /* If a reply is available from the higher layers */ + /* within Treply_delay after the reception of the */ + /* final octet of the requesting frame */ + /* (the mechanism used to determine this is a local matter), */ + /* then call MSTP_Send_Frame to transmit the reply frame */ + /* and enter the IDLE state to wait for the next frame. */ + uint8_t frame_type; + if (MSTP_Flag.TransmitPacketDER) { + frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY; + } else { + frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY; + } + MSTP_Send_Frame(frame_type, TransmitPacketDest, This_Station, + (uint8_t *) & TransmitPacket[0], TransmitPacketLen); + MSTP_Flag.TransmitPacketPending = false; + 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_Send_Frame to transmit a Reply Postponed frame, */ + /* and enter the IDLE state. */ + MSTP_Send_Frame(FRAME_TYPE_REPLY_POSTPONED, SourceAddress, + This_Station, NULL, 0); + Master_State = MSTP_MASTER_STATE_IDLE; + } + /* clear our flag we were holding for comparison */ + MSTP_Flag.ReceivedValidFrame = false; + break; + default: + Master_State = MSTP_MASTER_STATE_IDLE; + break; + } + + return transition_now; +} + +/* 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 */ + uint8_t * pdu, /* any data to be sent - may be null */ + unsigned pdu_len) +{ /* number of bytes of data */ + int bytes_sent = 0; + + if (MSTP_Flag.TransmitPacketPending == false) { + MSTP_Flag.TransmitPacketDER = npdu_data->data_expecting_reply; + TransmitPacket = pdu; + TransmitPacketLen = pdu_len; + bytes_sent = pdu_len; + TransmitPacketDest = dest->mac[0]; + MSTP_Flag.TransmitPacketPending = true; + } + + return bytes_sent; +} + +/* Return the length of the packet */ +uint16_t dlmstp_receive( + BACNET_ADDRESS * src, /* source address */ + uint8_t * pdu, /* PDU data */ + uint16_t max_pdu, /* amount of space available in the PDU */ + unsigned timeout) +{ /* milliseconds to wait for a packet */ + uint16_t pdu_len = 0; /* return value */ + + /* set the input buffer to the same data storage for zero copy */ + if (!InputBuffer) { + InputBuffer = pdu; + InputBufferSize = max_pdu; + } + /* only do receive state machine while we don't have a frame */ + if ((MSTP_Flag.ReceivedValidFrame == false) && + (MSTP_Flag.ReceivedInvalidFrame == false)) { + for (;;) { + MSTP_Receive_Frame_FSM(); + if (MSTP_Flag.ReceivedValidFrame || MSTP_Flag.ReceivedInvalidFrame) + break; + /* if we are not idle, then we are + receiving a frame or timing out */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) + break; + } + } + /* only do master state machine while rx is idle */ + if (Receive_State == MSTP_RECEIVE_STATE_IDLE) { + while (MSTP_Master_Node_FSM()) { + /* do nothing while some states fast transition */ + }; + } + /* if there is a packet that needs processed, do it now. */ + if (MSTP_Flag.ReceivePacketPending) { + MSTP_Flag.ReceivePacketPending = false; + pdu_len = DataLength; + src->mac_len = 1; + src->mac[0] = SourceAddress; + /* data is already in the pdu pointer */ + } + + return pdu_len; +} + +void dlmstp_set_mac_address( + uint8_t mac_address) +{ + /* Master Nodes can only have address 0-127 */ + if (mac_address <= 127) { + This_Station = mac_address; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + mac_address, + EEPROM_MSTP_MAC_ADDR); */ + if (mac_address > Nmax_master) + dlmstp_set_max_master(mac_address); + } + + return; +} + +uint8_t dlmstp_mac_address( + void) +{ + return 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. */ +void dlmstp_set_max_info_frames( + uint8_t max_info_frames) +{ + if (max_info_frames >= 1) { + Nmax_info_frames = max_info_frames; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + (uint8_t)max_info_frames, + EEPROM_MSTP_MAX_INFO_FRAMES_ADDR); */ + } + + return; +} + +uint8_t dlmstp_max_info_frames( + void) +{ + return 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. */ +void dlmstp_set_max_master( + uint8_t max_master) +{ + if (max_master <= 127) { + if (This_Station <= max_master) { + Nmax_master = max_master; + /* FIXME: implement your data storage */ + /* I2C_Write_Byte( + EEPROM_DEVICE_ADDRESS, + max_master, + EEPROM_MSTP_MAX_MASTER_ADDR); */ + } + } + + return; +} + +uint8_t dlmstp_max_master( + void) +{ + return Nmax_master; +} + +void dlmstp_get_my_address( + BACNET_ADDRESS * my_address) +{ + int i = 0; /* counter */ + + my_address->mac_len = 1; + my_address->mac[0] = This_Station; + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; + for (i = 0; i < MAX_MAC_LEN; i++) { + my_address->adr[i] = 0; + } + + return; +} + +void dlmstp_get_broadcast_address( + BACNET_ADDRESS * dest) +{ /* destination address */ + int i = 0; /* counter */ + + if (dest) { + dest->mac_len = 1; + dest->mac[0] = MSTP_BROADCAST_ADDRESS; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* always zero when DNET is broadcast */ + for (i = 0; i < MAX_MAC_LEN; i++) { + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack/ports/dos/extkword.h b/bacnet-stack/ports/dos/extkword.h new file mode 100644 index 00000000..40bdf488 --- /dev/null +++ b/bacnet-stack/ports/dos/extkword.h @@ -0,0 +1,131 @@ +/*==================================================================== + + _MSC_VER Microsoft C 6.0 and later + _QC Microsoft Quick C 2.51 and later + __TURBOC__ Borland Turbo C, Turbo C++ and BC++ + __BORLANDC__ Borland C++ + __ZTC__ Zortech C and C++ + __SC__ Symantec C++ + __WATCOMC__ WATCOM C + __POWERC Mix Power C + __GNUC__ Gnu C + + Revised: + + 25-Sep-95 Bob Stout Original from PC-PORT.H + 30-Mar-96 Ed Blackman OS/2 mods for OS/2 ver 2.0 and up + 30-May-96 Andrew Clarke Added support for WATCOM C/C++ __NT__ macro. + 17-Jun-96 Bob Stout Added __FLAT__ macros support + 20-Aug-96 Bob Stout Eliminate Win32 conflicts +======================================================================*/ + + +/* prevent multiple inclusions of this header file */ + +#ifndef EXTKWORD__H +#define EXTKWORD__H + +#include /* For INT_MAX, LONG_MAX */ + +/* +** Watcom defines __FLAT__ for 32-bit environments and so will we +*/ + +#if !defined(__FLAT__) && !defined(__WATCOMC__) && !defined(_MSC_VER) + #if defined(__GNUC__) + #define __FLAT__ 1 + #elif defined (_WIN32) || defined(WIN32) || defined(__NT__) + #define __FLAT__ 1 + #elif defined(__INTSIZE) + #if (4 == __INTSIZE) + #define __FLAT__ 1 + #endif + #elif (defined(__ZTC__) && !defined(__SC__)) || defined(__TURBOC__) + #if ((INT_MAX != SHRT_MAX) && (SHRT_MAX == 32767)) + #define __FLAT__ 1 + #endif + #endif +#endif + +/* +** Correct extended keywords syntax +*/ + +#if defined(__unix__) + #if !defined(FAR) + #define FAR + #endif + #if !defined(NEAR) + #define NEAR + #endif + #if !defined(HUGE) + #define HUGE + #endif + #if !defined(PASCAL) + #define PASCAL + #endif + #if !defined(CDECL) + #define CDECL + #endif + #if !defined(INTERRUPT) + #define INTERRUPT + #endif +#elif defined(__OS2__) /* EBB: not sure this works for OS/2 1.x */ + #include + #define INTERRUPT + #ifndef HUGE + #define HUGE + #endif +#elif defined(_WIN32) || defined(WIN32) || defined(__NT__) + #define WIN32_LEAN_AND_MEAN + #define NOGDI + #define NOSERVICE + #undef INC_OLE1 + #undef INC_OLE2 + #include + #define INTERRUPT + #ifndef HUGE + #define HUGE + #endif +#else /* ! Win 32 or OS/2 */ + #if (defined(__POWERC) || (defined(__TURBOC__) && !defined(__BORLANDC__)) \ + || (defined(__ZTC__) && !defined(__SC__))) && !defined(__FLAT__) + #define FAR far + #define NEAR near + #define PASCAL pascal + #define CDECL cdecl + #if (defined(__ZTC__) && !defined(__SC__)) || (defined(__SC__) && \ + (__SC__ < 0x700)) + #ifndef HUGE + #define HUGE far + #endif + #define INTERRUPT + #else + #ifndef HUGE + #define HUGE huge + #endif + #define INTERRUPT interrupt + #endif + #else + #if (defined(__MSDOS__) || defined(MSDOS)) && !defined(__FLAT__) + #define FAR _far + #define NEAR _near + #ifndef HUGE + #define HUGE _huge + #endif + #define PASCAL _pascal + #define CDECL _cdecl + #define INTERRUPT _interrupt + #else + #define FAR + #define NEAR + #ifndef HUGE + #define HUGE + #endif + #define PASCAL + #define CDECL + #endif + #endif +#endif + +#endif /* EXTKWORD__H */ diff --git a/bacnet-stack/ports/dos/mk_fp.h b/bacnet-stack/ports/dos/mk_fp.h new file mode 100644 index 00000000..fea21eb6 --- /dev/null +++ b/bacnet-stack/ports/dos/mk_fp.h @@ -0,0 +1,23 @@ +/* +** MK_FP.H +** +** Standard header file making sure this pesky Intel macro is defined! +*/ + +#ifndef MK_FP__H +#define MK_FP__H + +#include "extkword.h" + +#if defined(__WATCOMC__) + #include +#elif !defined(__PACIFIC__) + #include +#endif + +#if !defined(MK_FP) + #define MK_FP(seg,off) \ + ((void FAR *)(((unsigned long)(seg) << 16)|(unsigned)(off))) +#endif + +#endif /* MK_FP__H */ diff --git a/bacnet-stack/ports/dos/pchwio.c b/bacnet-stack/ports/dos/pchwio.c new file mode 100644 index 00000000..b240eb42 --- /dev/null +++ b/bacnet-stack/ports/dos/pchwio.c @@ -0,0 +1,70 @@ +/* +** PCHWIO.C - SNIPPETS portable hardware I/O access under DOS +** +** public domain by Bob Stout +*/ + +#include "pchwio.h" +#include "mk_fp.h" + +#if defined(__ZTC__) && !defined(__SC__) + +void FAR * getvect(unsigned intnum) +{ + unsigned seg, off; + + int_getvector(intnum, &off, &seg); + return MK_FP(seg, off); +} + +void setvect(unsigned intnum, void (INTERRUPT FAR *handler)()) +{ + unsigned seg = FP_SEG(handler), off = FP_OFF(handler); + + int_setvector(intnum, off, seg); +} + +#endif /* ZTC getvect(), setvect() */ + + + +#if defined(_MSC_VER) || defined(__WATCOMC__) || \ + defined(__ZTC__) || defined(__SC__) + +#if !defined(MK_FP) + #define MK_FP(seg,off) ((void far *)(((long)(seg) << 16)|(unsigned)(off))) +#endif + +unsigned char Peekb(unsigned seg, unsigned ofs) +{ + unsigned char FAR *ptr; + + ptr = MK_FP(seg, ofs); + return *ptr; +} + +unsigned short Peekw(unsigned seg, unsigned ofs) +{ + unsigned FAR *ptr; + + ptr = MK_FP(seg, ofs); + return *ptr; +} + +void Pokeb(unsigned seg, unsigned ofs, unsigned char ch) +{ + unsigned char FAR *ptr; + + ptr = MK_FP(seg, ofs); + *ptr = ch; +} + +void Pokew(unsigned seg, unsigned ofs, unsigned short num) +{ + unsigned FAR *ptr; + + ptr = MK_FP(seg, ofs); + *ptr = num; +} + +#endif /* MSC/ZTC/WC Peek(), poke() */ diff --git a/bacnet-stack/ports/dos/pchwio.h b/bacnet-stack/ports/dos/pchwio.h new file mode 100644 index 00000000..93d488fb --- /dev/null +++ b/bacnet-stack/ports/dos/pchwio.h @@ -0,0 +1,65 @@ +/* +** PCHWIO.H - SNIPPETS header file for portable hardware I/O access under DOS +** +** public domain by Bob Stout +*/ + +#ifndef PCHWIO__H +#define PCHWIO__H + +#include +#include "extkword.h" + + +#if defined(__TURBOC__) || defined(__POWERC) + #ifndef inp + #define inp inportb + #endif + #ifndef outp + #define outp outportb + #endif + #ifndef inpw + #define inpw inport + #endif + #ifndef outpw + #define outpw outport + #endif +#elif defined(__ZTC__) + #include + #define enable int_on + #define disable int_off + #if !defined(__SC__) + void FAR * getvect(unsigned intnum); + void setvect(unsigned intnum, void (INTERRUPT FAR *handler)()); + #else + #define getvect _dos_getvect + #define setvect _dos_setvect + #endif +#else /* assume MSC/QC/WC */ + #include + #if defined(__WATCOMC__) + #include + #endif + #define enable _enable + #define disable _disable + #define getvect _dos_getvect + #define setvect _dos_setvect +#endif + + +#if defined(_MSC_VER) || defined(__WATCOMC__) || \ + defined(__ZTC__) || defined(__SC__) + +unsigned char Peekb(unsigned seg, unsigned ofs); /* PCHWIO.C */ +unsigned short Peekw(unsigned seg, unsigned ofs); /* PCHWIO.C */ +void Pokeb(unsigned seg, unsigned ofs, unsigned char ch); /* PCHWIO.C */ +void Pokew(unsigned seg, unsigned ofs, unsigned short num); /* PCHWIO.C */ + +#elif defined(__TURBOC__) + #define Peekw peek + #define Pokew poke + #define Peekb peekb + #define Pokeb pokeb +#endif /* peek(), poke() */ + +#endif /* PCHWIO__H */ diff --git a/bacnet-stack/ports/dos/queue.c b/bacnet-stack/ports/dos/queue.c new file mode 100644 index 00000000..f65dcaf0 --- /dev/null +++ b/bacnet-stack/ports/dos/queue.c @@ -0,0 +1,61 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Queue.c | +| Date : September 29, 1994 | +| Time : 10:16AM | +| Revision : 1.0 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#include + +#include "queue.h" + +QUEUE *alloc_queue( int size) +{ QUEUE *retval; + retval = (QUEUE *) malloc( sizeof( QUEUE) + (size_t) size); + if ( (QUEUE *) 0 != retval) + { retval->size = size; + retval->head = 0; + retval->tail = 0; + retval->avail = size; + retval->buffer = ( (char *) retval) + sizeof( QUEUE); + } + return retval; +} + +int en_queue( QUEUE *queue, char data) +{ int retval = -1; + if ( 0 != queue->avail) + { *( queue->buffer + queue->head) = data; + queue->head += 1; + if ( queue->head == queue->size) + queue->head = 0; + queue->avail -= 1; + retval = queue->avail; + } + return retval; +} + +int de_queue( QUEUE *queue) +{ int retval = -1; + if ( queue->avail != queue->size) + { retval = *( queue->buffer + queue->tail); + queue->tail += 1; + if ( queue->tail == queue->size) + queue->tail = 0; + queue->avail += 1; + } + return retval; +} + +/* End of Queue.c */ diff --git a/bacnet-stack/ports/dos/queue.h b/bacnet-stack/ports/dos/queue.h new file mode 100644 index 00000000..0a3bcd39 --- /dev/null +++ b/bacnet-stack/ports/dos/queue.h @@ -0,0 +1,41 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : QUEUE.H | +| Date : August 30, 1994 | +| Time : 5:40 PM | +| Revision : 0.0 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#ifndef QUEUE__H +#define QUEUE__H + +/* Needed by Serial.C */ + +typedef struct +{ int size; + int head; + int tail; + int avail; + char *buffer; +} QUEUE; + +#define queue_empty(queue) (queue)->head == (queue)->tail +#define queue_avail(queue) (queue)->avail + +QUEUE *alloc_queue( int size); +int en_queue( QUEUE *queue_ptr, char data); +int de_queue( QUEUE *queue_ptr); + +/* End of Queue.H */ + +#endif /* QUEUE__H */ diff --git a/bacnet-stack/ports/dos/readme.txt b/bacnet-stack/ports/dos/readme.txt new file mode 100644 index 00000000..2ce90d17 --- /dev/null +++ b/bacnet-stack/ports/dos/readme.txt @@ -0,0 +1,5 @@ +This is a port to DOS using the BACnet MS/TP datalink layer. +It utilizes some serial routines from snippets.org. +It was tested and compiled with Turbo C++ 1.01 which is +freely available from http://dn.codegear.com/article/21751 +It was targeting the TS-3100 from Technologic Systems. diff --git a/bacnet-stack/ports/dos/rs485.c b/bacnet-stack/ports/dos/rs485.c new file mode 100644 index 00000000..b0595daa --- /dev/null +++ b/bacnet-stack/ports/dos/rs485.c @@ -0,0 +1,284 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* RS-485 initialization on AT91SAM7S inspired by Keil Eletronik serial.c +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +/* 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 +#include "timer.h" + +/* This file has been customized for use with DOS */ +#include "hardware.h" + +/* UART */ +static volatile AT91S_USART *RS485_Interface = AT91C_BASE_US0; +/* baud rate */ +static int RS485_Baud = 38400; + +/* The minimum time after the end of the stop bit of the final octet of a */ +/* received frame before a node may enable its EIA-485 driver: 40 bit times. */ +/* At 9600 baud, 40 bit times would be about 4.166 milliseconds */ +/* At 19200 baud, 40 bit times would be about 2.083 milliseconds */ +/* At 38400 baud, 40 bit times would be about 1.041 milliseconds */ +/* At 57600 baud, 40 bit times would be about 0.694 milliseconds */ +/* At 76800 baud, 40 bit times would be about 0.520 milliseconds */ +/* At 115200 baud, 40 bit times would be about 0.347 milliseconds */ +/* 40 bits is 4 octets including a start and stop bit with each octet */ +#define Tturnaround (40UL) +/* turnaround_time_milliseconds = (Tturnaround*1000UL)/RS485_Baud; */ + +/**************************************************************************** +* DESCRIPTION: Initializes the RS485 hardware and variables, and starts in +* receive mode. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Initialize( + void) +{ + /* Enable the USART0 clock in the Power Management Controller */ + volatile AT91PS_PMC pPMC = AT91C_BASE_PMC; + pPMC->PMC_PCER = pPMC->PMC_PCSR | (1 << AT91C_ID_US0); + + /* Disable and clear USART0 interrupt + in AIC Interrupt Disable Command Register */ + volatile AT91PS_AIC pAIC = AT91C_BASE_AIC; + pAIC->AIC_IDCR = (1 << AT91C_ID_US0); + pAIC->AIC_ICCR = (1 << AT91C_ID_US0); + + /* enable the peripheral by disabling the pin in the PIO controller */ + *AT91C_PIOA_PDR = AT91C_PA5_RXD0 | AT91C_PA6_TXD0 | AT91C_PA7_RTS0; + + RS485_Interface->US_CR = AT91C_US_RSTRX | /* Reset Receiver */ + AT91C_US_RSTTX | /* Reset Transmitter */ + AT91C_US_RSTSTA | /* Clear status register */ + AT91C_US_RXDIS | /* Receiver Disable */ + AT91C_US_TXDIS; /* Transmitter Disable */ + + RS485_Interface->US_MR = AT91C_US_USMODE_RS485 | /* RS-485 Mode - RTS auto assert */ + AT91C_US_CLKS_CLOCK | /* Clock = MCK */ + AT91C_US_CHRL_8_BITS | /* 8-bit Data */ + AT91C_US_PAR_NONE | /* No Parity */ + AT91C_US_NBSTOP_1_BIT; /* 1 Stop Bit */ + + /* set the Time Guard to release RTS after x bit times */ + RS485_Interface->US_TTGR = 1; + + /* Receiver Time-out disabled */ + RS485_Interface->US_RTOR = 0; + + /* baud rate */ + RS485_Interface->US_BRGR = MCK / 16 / RS485_Baud; + + RS485_Interface->US_CR = AT91C_US_RXEN | /* Receiver Enable */ + AT91C_US_TXEN; /* Transmitter Enable */ + + return; +} + +void RS485_Cleanup( + void) +{ + +} + +/**************************************************************************** +* DESCRIPTION: Returns the baud rate that we are currently running at +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +uint32_t RS485_Get_Baud_Rate( + void) +{ + return RS485_Baud; +} + +/**************************************************************************** +* DESCRIPTION: Sets the baud rate for the chip USART +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_Set_Baud_Rate( + uint32_t baud) +{ + bool valid = true; + + switch (baud) { + case 9600: + case 19200: + case 38400: + case 57600: + case 76800: + case 115200: + RS485_Baud = baud; + RS485_Interface->US_BRGR = MCK / 16 / baud; + /* FIXME: store the baud rate */ + break; + default: + valid = false; + break; + } + + return valid; +} + +/**************************************************************************** +* DESCRIPTION: Waits on the SilenceTimer for 40 bits. +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Turnaround_Delay( + void) +{ + uint16_t turnaround_time; + + /* delay after reception before trasmitting - per MS/TP spec */ + /* wait a minimum 40 bit times since reception */ + /* at least 1 ms for errors: rounding, clock tick */ + turnaround_time = 1 + ((Tturnaround * 1000UL) / RS485_Baud); + while (Timer_Silence() < turnaround_time) { + /* do nothing - wait for timer to increment */ + }; +} + +/**************************************************************************** +* DESCRIPTION: Enable or disable the transmitter +* RETURN: none +* ALGORITHM: none +* NOTES: The Atmel ARM7 has an automatic enable/disable in RS485 mode. +*****************************************************************************/ +void RS485_Transmitter_Enable( + bool enable) +{ + (void) enable; +} + +/**************************************************************************** +* DESCRIPTION: Send some data and wait until it is sent +* RETURN: none +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes) +{ /* number of bytes of data */ + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + /* LED ON */ + pPIO->PIO_CODR = LED1; + /* send all the bytes */ + while (nbytes) { + while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) { + /* do nothing - wait until Tx buffer is empty */ + } + RS485_Interface->US_THR = *buffer; + buffer++; + nbytes--; + } + while (!(RS485_Interface->US_CSR & AT91C_US_TXRDY)) { + /* do nothing - wait until Tx buffer is empty */ + } + /* per MSTP spec */ + Timer_Silence_Reset(); +} + +/**************************************************************************** +* DESCRIPTION: Return true if a framing or overrun error is present +* RETURN: true if error +* ALGORITHM: none +* NOTES: Clears any error flags. +*****************************************************************************/ +bool RS485_ReceiveError( + void) +{ + bool ReceiveError = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + /* check for data or error */ + if (RS485_Interface->US_CSR & (AT91C_US_OVRE | AT91C_US_FRAME)) { + /* clear the error flag */ + RS485_Interface->US_CR = AT91C_US_RSTSTA; + ReceiveError = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return ReceiveError; +} + +/**************************************************************************** +* DESCRIPTION: Return true if data is available +* RETURN: true if data is available, with the data in the parameter set +* ALGORITHM: none +* NOTES: none +*****************************************************************************/ +bool RS485_DataAvailable( + uint8_t * DataRegister) +{ + bool DataAvailable = false; + /* LED on send */ + volatile AT91PS_PIO pPIO = AT91C_BASE_PIOA; + + if (RS485_Interface->US_CSR & AT91C_US_RXRDY) { + /* data is available */ + *DataRegister = RS485_Interface->US_RHR; + DataAvailable = true; + /* LED ON */ + pPIO->PIO_CODR = LED2; + } + + return DataAvailable; +} + +#ifdef TEST_RS485 +int main( + void) +{ + unsigned i = 0; + uint8_t DataRegister; + + RS485_Set_Baud_Rate(38400); + RS485_Initialize(); + /* receive task */ + for (;;) { + if (RS485_ReceiveError()) { + fprintf(stderr, "ERROR "); + } else if (RS485_DataAvailable(&DataRegister)) { + fprintf(stderr, "%02X ", DataRegister); + } + } +} +#endif diff --git a/bacnet-stack/ports/dos/rs485.h b/bacnet-stack/ports/dos/rs485.h new file mode 100644 index 00000000..ae622daa --- /dev/null +++ b/bacnet-stack/ports/dos/rs485.h @@ -0,0 +1,60 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef RS485_H +#define RS485_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void RS485_Initialize( + void); + + void RS485_Transmitter_Enable( + bool enable); + + void RS485_Send_Data( + uint8_t * buffer, /* data to send */ + uint16_t nbytes); /* number of bytes of data */ + + bool RS485_ReceiveError( + void); + bool RS485_DataAvailable( + uint8_t * data); + + void RS485_Turnaround_Delay( + void); + uint32_t RS485_Get_Baud_Rate( + void); + bool RS485_Set_Baud_Rate( + uint32_t baud); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/ports/dos/serial.c b/bacnet-stack/ports/dos/serial.c new file mode 100644 index 00000000..19ca9a57 --- /dev/null +++ b/bacnet-stack/ports/dos/serial.c @@ -0,0 +1,342 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Serial.c | +| Date : October 24, 1991 | +| Time : 15:03 | +| Revision : 1.1 | +| Update: August 29, 1994 | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +/* ++----------------------------------------------------------+ +| Call open_serial to install the interrupt handler | +| You must call close_serial before exiting your program | +| or a machine crash will occur! | ++----------------------------------------------------------+ +*/ + +#include +#include +#include +#include "serial.h" +#include "queue.h" + +QUEUE *Serial_In_Queue; +QUEUE *Serial_Out_Queue; + +OLD_COMM_PARAMS old_comm_params; +COMM_STATUS comm_status; + +void (INTERRUPT FAR *oldvector_serial )(); +/* save addr for intr handler */ + +int ComBase; /* Comm port address */ +int IrqNum; /* Comm interrupt request */ + +int OpenComPort ( char Port ) /* install int. handler */ +{ + unsigned status; + int retval = -1; + + /* allocate input and output queues */ + + Serial_In_Queue = alloc_queue( SerInBufSize ); + if ( (QUEUE *) 0 == Serial_In_Queue) + return retval; + Serial_Out_Queue = alloc_queue( SerOutBufSize ); + if ( (QUEUE *) 0 == Serial_Out_Queue) + { + free ( Serial_In_Queue ); + return retval; + } + retval = 0; + + /* Setup Comm base port address and IRQ number */ + + switch ( Port) + { + case '1': ComBase = 0x3F8; IrqNum = 4; break; + case '2': ComBase = 0x2F8; IrqNum = 3; break; + case '3': ComBase = 0x3E8; IrqNum = 4; break; + case '4': ComBase = 0x2E8; IrqNum = 3; break; + default : ComBase = 0x3F8; IrqNum = 4; break; + } + old_comm_params.int_enable = inp ( ComBase + INT_EN ); + outp ( ComBase + INT_EN, 0 ); /* turn off comm interrupts */ + + /* save old comm parameters */ + + old_comm_params.line = inp ( ComBase + LINE_CNTRL ); + old_comm_params.modem = inp ( ComBase + MODEM_CNTRL ); + status = inp ( ComBase + LINE_CNTRL ); + outp ( ComBase + LINE_CNTRL, (unsigned char) status | 0x80 ); + old_comm_params.baud_lsb = inp ( ComBase + BAUD_LSB ); + old_comm_params.baud_msb = inp ( ComBase + BAUD_MSB ); + status = inp ( ComBase + LINE_CNTRL ); + outp ( ComBase + LINE_CNTRL, (unsigned char) status | 0x7F ); + status = OUT2 | DTR; /* DTR/OUT2 must be set! */ + outp ( ComBase + MODEM_CNTRL, (unsigned char) status ); + + /* get serial port address/vector */ + + oldvector_serial = (void(INTERRUPT FAR *)(void))getvect(IrqNum + 8 ); + + /* set our interrupt handler */ + + setvect ( IrqNum + 8, serial ); + + /* save the PIC */ + + old_comm_params.int_cntrl = inp ( 0x21 ); + status = ( 1 << IrqNum); /* calculate int enable bit */ + status = ~status; + + /* ok enable comm ints */ + + outp ( 0x21, (unsigned char) old_comm_params.int_cntrl & + (unsigned char) status ); + return retval; +} + +void CloseComPort ( void ) +{ + int status; + + /* restore UART to previous state */ + + outp ( ComBase + INT_EN, (unsigned char) 0 ); + outp ( ComBase + MODEM_CNTRL, + (unsigned char) old_comm_params.modem ); + status = inp ( ComBase + LINE_CNTRL ); + outp ( ComBase + LINE_CNTRL, + (unsigned char) status | 0x80 ); + outp ( ComBase + BAUD_LSB, + (unsigned char) old_comm_params.baud_lsb ); + outp ( ComBase + BAUD_MSB, + (unsigned char) old_comm_params.baud_msb ); + outp ( ComBase + LINE_CNTRL, + (unsigned char) old_comm_params.line ); + outp ( 0x21, (unsigned char) old_comm_params.int_cntrl ); + + /* restore old interrupt handler */ + + setvect ( IrqNum + 8, oldvector_serial ); + + /* free input and output queues */ + + free ( Serial_In_Queue ); + free ( Serial_Out_Queue ); + return; +} + +void InitComPort ( char Baud[], char Databits, + char Parity, char Stopbits ) +{ + int status; + long baudrate; + unsigned divisor; + + /* set baud rate */ + + status = inp ( ComBase + LINE_CNTRL ); + outp ( ComBase + LINE_CNTRL, + (unsigned char) status | 0x80 ); + baudrate = atol ( Baud ); + if ( baudrate == 0) + baudrate = 2400L; + divisor = (unsigned) ( 115200L / baudrate); + outp ( ComBase + BAUD_LSB, + (unsigned char) ( divisor & 0x00FF) ); + outp ( ComBase + BAUD_MSB, + (unsigned char) ( ( divisor >> 8) & 0x00FF) ); + status = 0x00; + + /* set parity */ + + switch ( Parity) /* set parity value */ + { + case 'O': /* odd parity */ + case 'o': + status = 0x08; break; + + case 'E': /* even parity */ + case 'e': + status = 0x18; break; + + case 'S': /* stick parity */ + case 's': + status = 0x28; break; + + case 'N': /* no parity */ + case 'n': + default : status = 0x00; + } + + /* set number data bits */ + + switch ( Databits) + { + case '5': + break; + + case '6': + status = status | 0x01; + break; + + case '7': + status = status | 0x02; + break; + + case '8': + default : + status = status | 0x03; + } + + /* set number stop bits */ + + switch ( Stopbits) + { + case '2': + status = status | 0x04; + break; + + case '1': + default : + ; + } + outp ( ComBase + LINE_CNTRL, (unsigned char) status ); + status = OUT2 | DTR; /* DTR/OUT2 must be set! */ + outp ( ComBase + MODEM_CNTRL, (unsigned char) status ); + + /* enable serial interrupts */ + + outp ( ComBase + INT_EN, RX_INT | ERR_INT | RS_INT ); + return; +} + +void DropDtr ( void ) +{ + int status; + + status = inp ( ComBase + MODEM_CNTRL ); + status &= 0xFE; /* turn off DTR bit */ + outp ( ComBase + MODEM_CNTRL, (unsigned char) status ); + return; +} + +void RaiseDtr ( void ) +{ + int status; + + status = inp ( ComBase + MODEM_CNTRL ); + status |= 0x01; /* turn on DTR bit */ + outp ( ComBase + MODEM_CNTRL, (unsigned char) status ); + return; +} + +int ComRecChar ( void ) +{ + return de_queue ( Serial_In_Queue ); +} + +int ComSendString ( char *string ) +{ + int retval; + char *pointer; + pointer = string; + + while ( *pointer) + { + retval = en_queue ( Serial_Out_Queue, *pointer ); + pointer++; + } + if ( 0x0 == (comm_status.modem & 0x40)) + RaiseDtr (); + outp ( ComBase + INT_EN, RX_INT | TBE_INT | ERR_INT | RS_INT ); + return retval; +} + +int ComSendChar ( char character ) +{ + int retval; + + /* interrupt driven send */ + + if ( 0x0 == (comm_status.modem & 0x40)) + RaiseDtr (); + retval = en_queue ( Serial_Out_Queue, character ); + if ( - 1 != retval) + outp ( ComBase + INT_EN, RX_INT | TBE_INT | ERR_INT | RS_INT ); + return retval; +} + +int ComStatus ( void ) +{ + unsigned status; + unsigned retval; + + retval = inp ( ComBase + LINE_STATUS ); + retval = retval << 8; + status = inp ( ComBase + MODEM_STATUS ); + retval = retval | status; + if ( queue_empty ( Serial_In_Queue )) + retval &= 0xFEFF; + else retval |= 0x0100; + return (int) retval; +} + +void INTERRUPT FAR serial ( void ) /* interrupt handler */ +{ + int temp; + + disable (); + while ( 1) + { + comm_status.intrupt = inp ( ComBase + INT_ID ); + comm_status.intrupt &= 0x0f; + switch ( comm_status.intrupt) + { + case 0x00: /* modem interrupt */ + comm_status.modem = inp( ComBase + MODEM_STATUS ); + break; + + case 0x02: /* xmit interrupt */ + if ( queue_empty ( Serial_Out_Queue )) + outp(ComBase + INT_EN, RX_INT|ERR_INT|RS_INT ); + else + { + temp = de_queue ( Serial_Out_Queue ); + if ( - 1 != temp) + outp ( ComBase + XMIT, temp ); + } + break; + + case 0x04: /* receive interrupt */ + en_queue(Serial_In_Queue, (char)inp(ComBase + REC)); + break; + + case 0x06: /* line interrupt */ + comm_status.line = inp ( ComBase + LINE_STATUS ); + (void) inp ( ComBase + REC ); + en_queue ( Serial_In_Queue, '!' ); + break; + + default: /* No Mo` Left */ + comm_status.modem = inp ( ComBase + MODEM_STATUS ); + outp ( 0x20, 0x20 ); + enable (); + return; + } /* switch */ + } /* while */ +} +/* End of Serial.C */ diff --git a/bacnet-stack/ports/dos/serial.h b/bacnet-stack/ports/dos/serial.h new file mode 100644 index 00000000..9b4b511e --- /dev/null +++ b/bacnet-stack/ports/dos/serial.h @@ -0,0 +1,106 @@ +/* ++----------------------------------------------------+ +| Thunderbird Software | ++----------------------------------------------------+ +| Filespec : Serial.c | +| Date : October 24, 1991 | +| Time : 15:03 | +| Revision : 1.1 | +| Update : August 29, 1994 | +| Update : March 12, 1995 by Bob Stout | ++----------------------------------------------------+ +| Programmer: Scott Andrews | +| Address : 5358 Summit RD SW | +| City/State: Pataskala, Ohio | +| Zip : 43062 | ++----------------------------------------------------+ +| Released to the Public Domain | ++----------------------------------------------------+ +*/ + +#ifndef SERIAL__H +#define SERIAL__H + +#include "extkword.h" +#include "pchwio.h" + +#define SerInBufSize 4096 /* Size of input buffer */ +#define SerOutBufSize 512 /* Size of output buffer */ + +/* 8250 registers */ + +#define REC 0 /* Uart receive reg. */ +#define XMIT 0 /* Uart transmit reg. */ +#define INT_EN 1 /* Uart int. enable reg. */ +#define INT_ID 2 /* Uart int. ident. reg. */ +#define LINE_CNTRL 3 /* Uart line control reg. */ +#define MODEM_CNTRL 4 /* Uart modem control reg. */ +#define LINE_STATUS 5 /* Uart line status reg. */ +#define MODEM_STATUS 6 /* Uart modem status reg. */ +#define BAUD_LSB 0 /* Uart baud divisor reg. */ +#define BAUD_MSB 1 /* Uart baud divisor reg. */ + +#define NONE 0 /* Handshake param none */ +#define HDW 1 /* Handshake param hardware */ +#define XON 2 /* Handshake param software */ + +/* Interrupt enable register */ + +#define RX_INT 0x01 /* Receive interrupt mask */ +#define TBE_INT 0x02 /* Transmit buffer empty mask */ +#define ERR_INT 0x04 /* Error interrupt mask */ +#define RS_INT 0x08 /* Line interrupt mask */ + +/* Interrupt id register */ + +#define OUT2 0x08 /* Out 2 line */ +#define DTR 0x01 /* DTR high */ +#define RTS 0x02 /* RTS high */ +#define CTS 0x10 +#define DSR 0x20 +#define XMTRDY 0x20 +#define TXR 0 /* Transmit register (WRITE) */ + +#if !defined TRUE /* Define boolean true/false */ +#define FALSE 0 +#define TRUE !FALSE +#endif + +extern void (INTERRUPT FAR *oldvector_serial )( void); + +extern int ComBase; /* Comm port address */ +extern int IrqNum; /* Comm interrupt request */ + +typedef struct /* Save existing comm params */ +{ int int_enable; /* old interrupt enable reg value*/ + int line; /* " line control " " */ + int modem; /* old modem control " " */ + int baud_lsb; /* old baud rate divisor LSD */ + int baud_msb; /* " " " " MSD */ + int int_cntrl; /* old PIC interrupt reg value */ +} OLD_COMM_PARAMS; +extern OLD_COMM_PARAMS old_comm_params; + +typedef struct +{ int line; /* Uart line status reg. */ + int modem; /* Uart mode status reg. */ + int intrupt; /* Uart interrupt reg. */ + int handshake; /* Handshake status */ +} COMM_STATUS; /* status, updated, handler */ +extern COMM_STATUS comm_status; + +int OpenComPort ( char Port ); /*setup comm for usage */ +void InitComPort ( char Baud[], char Databits, char Parity, char Stop ); +void CloseComPort ( void ); /* Restore comm port */ +void DropDtr ( void ); /* Lower DTR */ +void RaiseDtr ( void ); /* Raise DTR */ +int ComRecChar ( void ); /* Fetch character from rcv buf*/ + +int ComSendChar ( char character ); /* Put char into xmit buffer */ +int ComSendString ( char *string ); +int ComStatus ( void ); /* Fetch comm status */ +void INTERRUPT FAR serial ( void ); /* interrupt handler */ + +/* End of Serial.H */ + +#endif /* SERIAL__H */ diff --git a/bacnet-stack/ports/dos/stdbool.h b/bacnet-stack/ports/dos/stdbool.h new file mode 100644 index 00000000..badcfd0a --- /dev/null +++ b/bacnet-stack/ports/dos/stdbool.h @@ -0,0 +1,20 @@ +#ifndef _STDBOOL_H +#define _STDBOOL_H + +/* C99 Boolean types for compilers without C99 support */ +/* http://www.opengroup.org/onlinepubs/009695399/basedefs/stdbool.h.html */ +#if !defined(__cplusplus) + +#if !defined(__GNUC__) +/* _Bool builtin type is included in GCC */ +typedef enum { _Bool_must_promote_to_int = -1, false = 0, true = 1 } _Bool; +#endif + +#define bool _Bool +#define true 1 +#define false 0 +#define __bool_true_false_are_defined 1 + +#endif + +#endif diff --git a/bacnet-stack/ports/dos/stdint.h b/bacnet-stack/ports/dos/stdint.h new file mode 100644 index 00000000..ca6a52de --- /dev/null +++ b/bacnet-stack/ports/dos/stdint.h @@ -0,0 +1,31 @@ +/* Defines the standard integer types that are used in code */ +/* for the x86 processor and Borland Compiler */ + +#ifndef _STDINT_H +#define _STDINT_H + +#include + +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; */ + +#define INT8_MIN (-128) +#define INT16_MIN (-32768) +#define INT32_MIN (-2147483647 - 1) + +#define INT8_MAX 127 +#define INT16_MAX 32767 +#define INT32_MAX 2147483647 + +#define UINT8_MAX 0xff /* 255U */ +#define UINT16_MAX 0xffff /* 65535U */ +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#endif /* STDINT_H */ diff --git a/bacnet-stack/ports/dos/timer.c b/bacnet-stack/ports/dos/timer.c new file mode 100644 index 00000000..5f78faaf --- /dev/null +++ b/bacnet-stack/ports/dos/timer.c @@ -0,0 +1,172 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ +#include +#include +#include + +/* global variable counts milliseconds */ +volatile unsigned long Timer_Milliseconds; +/* MS/TP Silence Timer */ +static volatile int SilenceTime; +/* counts ticks */ +volatile unsigned long Timer_Milliseconds; + +#define RTC_CMD_ADDR 0x70 // RTC internal register offset goes here +#define RTC_DAT_ADDR 0x71 // RTC internal register R/W access here + +static uint8_t RTC_RS_Convert( + uint16_t hertz) +{ + uint8_t RS = 0; + /* from DS12887A datasheet + SELECT BITS tPI PERIODIC + REGISTER A INTERRUPT SQW OUTPUT + RS3 RS2 RS1 RS0 RATE FREQUENCY + --- --- --- --- ------------ ---------- + 0 0 0 0 None 0Hz + 0 0 0 1 3.90625ms 256Hz + 0 0 1 0 7.8125ms 128Hz + 0 0 1 1 122.070µs 8192Hz + 0 1 0 0 244.141µs 4096Hz + 0 1 0 1 488.281µs 2048Hz + 0 1 1 0 976.5625µs 1024Hz + 0 1 1 1 1.953125ms 512Hz + 1 0 0 0 3.90625ms 256Hz + 1 0 0 1 7.8125ms 128Hz + 1 0 1 0 15.625ms 64Hz + 1 0 1 1 31.25ms 32Hz + 1 1 0 0 62.5ms 16Hz + 1 1 0 1 125ms 8Hz + 1 1 1 0 250ms 4Hz + 1 1 1 1 500ms 2Hz + */ + /* FIXME: create a clever formula to replace switch */ + switch (hertz) { + case 8192: RS = 3; break; + case 4096: RS = 4; break; + case 2048: RS = 5; break; + case 1024: RS = 6; break; + case 512: RS = 7; break; + case 256: RS = 8; break; + case 128: RS = 9; break; + case 64: RS = 10; break; + case 32: RS = 11; break; + case 16: RS = 12; break; + case 8: RS = 13; break; + case 4: RS = 14; break; + case 2: RS = 15; break; + default: + break; + } + + return RS; +} + +/* setting for 8192 interrupts per second + which is an interrupt every 122uS. */ +#define INT_FREQ 8192 + +static void interrupt Timer_Interrupt_Handler( + void) +{ + static uint16_t Timer_Ticks = 0; + static uint16_t Elapsed_Milliseconds = 0; + uint16_t milliseconds = 0; + uint16_t diff = 0; + uint8_t temp_reg; + + Timer_Ticks++; + milliseconds = (Timer_Ticks * 1000)/INT_FREQ; + diff = milliseconds - Elapsed_Milliseconds; + if (diff >= 1) { + Elapsed_Milliseconds = milliseconds; + Timer_Milliseconds++; + if (SilenceTime < 60000) + SilenceTime++; + } + /* max resolution */ + if (Timer_Ticks >= INT_FREQ) { + Timer_Ticks = 0; + Elapsed_Milliseconds = 0; + } + + /* clear interrupt */ + outportb( RTC_CMD_ADDR, 0x0C ); // select RTC register C + temp_reg = inportb( RTC_DAT_ADDR ); // read RTC register C + /* signal end of interrupt to slave PIC */ + outportb( 0xA0, 0x20 ); + /* signal end of interrupt to master PIC */ + outportb( 0x20, 0x20 ); +} + +/* previous interrrupt vector */ +static void interrupt(*OldVector)(); + +void Timer_Cleanup(void) +{ + setvect(0x70,OldVector); +} + +void Timer_Init( + void) +{ + uint8_t RC = RTC_RS_Convert(INT_FREQ); + + /* get old interrupt vector to re-install on exit */ + OldVector = getvect(0x70); + /* disable interrupts */ + disable(); + /* set RTC int. vector for our routine */ + setvect(0x70,Timer_Interrupt_Handler); + /* set register B with PIE enabled */ + outportb( RTC_CMD_ADDR, 0x0B ); + outportb( RTC_DAT_ADDR, 0x42 ); + /* set register A to our frequency */ + outportb( RTC_CMD_ADDR, 0x0A ); + outportb( RTC_DAT_ADDR, (0x20 | (RC & 0x0F)) ); + /* re-enable system interrupts */ + enable(); + atexit(Timer_Cleanup); +} + +int Timer_Silence( + void) +{ + uint16_t time_value; + + disable(); + time_value = SilenceTime; + enable(); + + return time_value; +} + +void Timer_Silence_Reset( + void) +{ + disable(); + SilenceTime = 0; + enable(); +} diff --git a/bacnet-stack/ports/dos/timer.h b/bacnet-stack/ports/dos/timer.h new file mode 100644 index 00000000..05f6beaa --- /dev/null +++ b/bacnet-stack/ports/dos/timer.h @@ -0,0 +1,47 @@ +/************************************************************************** +* +* Copyright (C) 2007 Steve Karg +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be included +* in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +*********************************************************************/ + +#ifndef TIMER_H +#define TIMER_H + +#include + +extern volatile unsigned long Timer_Milliseconds; + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + void Timer_Init( + void); + int Timer_Silence( + void); + void Timer_Silence_Reset( + void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif diff --git a/bacnet-stack/src/apdu.c b/bacnet-stack/src/apdu.c index 2b60f200..68758dce 100644 --- a/bacnet-stack/src/apdu.c +++ b/bacnet-stack/src/apdu.c @@ -59,10 +59,10 @@ static BACNET_SERVICES_SUPPORTED SERVICE_SUPPORTED_CREATE_OBJECT, SERVICE_SUPPORTED_DELETE_OBJECT, SERVICE_SUPPORTED_READ_PROPERTY, - SERVICE_SUPPORTED_READ_PROPERTY_CONDITIONAL, - SERVICE_SUPPORTED_READ_PROPERTY_MULTIPLE, + SERVICE_SUPPORTED_READ_PROP_CONDITIONAL, + SERVICE_SUPPORTED_READ_PROP_MULTIPLE, SERVICE_SUPPORTED_WRITE_PROPERTY, - SERVICE_SUPPORTED_WRITE_PROPERTY_MULTIPLE, + SERVICE_SUPPORTED_WRITE_PROP_MULTIPLE, SERVICE_SUPPORTED_DEVICE_COMMUNICATION_CONTROL, SERVICE_SUPPORTED_PRIVATE_TRANSFER, SERVICE_SUPPORTED_TEXT_MESSAGE, @@ -178,7 +178,7 @@ void apdu_set_confirmed_simple_ack_handler( case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: case SERVICE_CONFIRMED_DELETE_OBJECT: case SERVICE_CONFIRMED_WRITE_PROPERTY: - case SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE: + case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: /* Remote Device Management Services */ case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: case SERVICE_CONFIRMED_TEXT_MESSAGE: @@ -208,8 +208,8 @@ void apdu_set_confirmed_ack_handler( /* Object Access Services */ case SERVICE_CONFIRMED_CREATE_OBJECT: case SERVICE_CONFIRMED_READ_PROPERTY: - case SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL: - case SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE: + case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: + case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: case SERVICE_CONFIRMED_READ_RANGE: /* Remote Device Management Services */ case SERVICE_CONFIRMED_PRIVATE_TRANSFER: @@ -350,7 +350,7 @@ void apdu_handler( case SERVICE_CONFIRMED_REMOVE_LIST_ELEMENT: case SERVICE_CONFIRMED_DELETE_OBJECT: case SERVICE_CONFIRMED_WRITE_PROPERTY: - case SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE: + case SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE: /* Remote Device Management Services */ case SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL: case SERVICE_CONFIRMED_REINITIALIZE_DEVICE: @@ -394,8 +394,8 @@ void apdu_handler( /* Object Access Services */ case SERVICE_CONFIRMED_CREATE_OBJECT: case SERVICE_CONFIRMED_READ_PROPERTY: - case SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL: - case SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE: + case SERVICE_CONFIRMED_READ_PROP_CONDITIONAL: + case SERVICE_CONFIRMED_READ_PROP_MULTIPLE: case SERVICE_CONFIRMED_READ_RANGE: case SERVICE_CONFIRMED_PRIVATE_TRANSFER: /* Virtual Terminal Services */ diff --git a/bacnet-stack/src/bactext.c b/bacnet-stack/src/bactext.c index 4609a061..7ea82477 100644 --- a/bacnet-stack/src/bactext.c +++ b/bacnet-stack/src/bactext.c @@ -53,11 +53,11 @@ INDTEXT_DATA bacnet_confirmed_service_names[] = { {SERVICE_CONFIRMED_CREATE_OBJECT, "Create-Object"}, {SERVICE_CONFIRMED_DELETE_OBJECT, "Delete-Object"}, {SERVICE_CONFIRMED_READ_PROPERTY, "Read-Property"}, - {SERVICE_CONFIRMED_READ_PROPERTY_CONDITIONAL, + {SERVICE_CONFIRMED_READ_PROP_CONDITIONAL, "Read-Property-Conditional"}, - {SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE, "Read-Property-Multiple"}, + {SERVICE_CONFIRMED_READ_PROP_MULTIPLE, "Read-Property-Multiple"}, {SERVICE_CONFIRMED_WRITE_PROPERTY, "Write-Property"}, - {SERVICE_CONFIRMED_WRITE_PROPERTY_MULTIPLE, "Write-Property-Multiple"}, + {SERVICE_CONFIRMED_WRITE_PROP_MULTIPLE, "Write-Property-Multiple"}, {SERVICE_CONFIRMED_DEVICE_COMMUNICATION_CONTROL, "Device-Communication-Control"}, {SERVICE_CONFIRMED_PRIVATE_TRANSFER, "Private-Transfer"}, @@ -979,10 +979,10 @@ INDTEXT_DATA bacnet_engineering_unit_names[] = { , {UNITS_LITERS_PER_HOUR, "liters-per-hour"} , - {UNITS_KILOWATT_HOURS_PER_SQUARE_METER, + {UNITS_KW_HOURS_PER_SQUARE_METER, "kilowatt-hours-per-square-meter"} , - {UNITS_KILOWATT_HOURS_PER_SQUARE_FOOT, "kilowatt-hours-per-square-foot"} + {UNITS_KW_HOURS_PER_SQUARE_FOOT, "kilowatt-hours-per-square-foot"} , {UNITS_MEGAJOULES_PER_SQUARE_METER, "megajoules-per-square-meter"} , diff --git a/bacnet-stack/src/rpm.c b/bacnet-stack/src/rpm.c index b208be76..c62ddb77 100644 --- a/bacnet-stack/src/rpm.c +++ b/bacnet-stack/src/rpm.c @@ -50,7 +50,7 @@ int rpm_encode_apdu_init( apdu[0] = PDU_TYPE_CONFIRMED_SERVICE_REQUEST; apdu[1] = encode_max_segs_max_apdu(0, MAX_APDU); apdu[2] = invoke_id; - apdu[3] = SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE; /* service choice */ + apdu[3] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */ apdu_len = 4; } @@ -209,7 +209,7 @@ int rpm_ack_encode_apdu_init( if (apdu) { apdu[0] = PDU_TYPE_COMPLEX_ACK; /* complex ACK service */ apdu[1] = invoke_id; /* original invoke id from request */ - apdu[2] = SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE; /* service choice */ + apdu[2] = SERVICE_CONFIRMED_READ_PROP_MULTIPLE; /* service choice */ apdu_len = 3; } @@ -415,7 +415,7 @@ int rpm_ack_decode_apdu( if (apdu[0] != PDU_TYPE_COMPLEX_ACK) return -1; *invoke_id = apdu[1]; - if (apdu[2] != SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE) + if (apdu[2] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE) return -1; offset = 3; if (apdu_len > offset) { @@ -444,7 +444,7 @@ int rpm_decode_apdu( return -1; /* apdu[1] = encode_max_segs_max_apdu(0, Device_Max_APDU_Length_Accepted()); */ *invoke_id = apdu[2]; /* invoke id - filled in by net layer */ - if (apdu[3] != SERVICE_CONFIRMED_READ_PROPERTY_MULTIPLE) + if (apdu[3] != SERVICE_CONFIRMED_READ_PROP_MULTIPLE) return -1; offset = 4;