diff --git a/bacnet-stack/ports/win32/bacnet.ide b/bacnet-stack/ports/win32/bacnet.ide new file mode 100644 index 00000000..14318abb Binary files /dev/null and b/bacnet-stack/ports/win32/bacnet.ide differ diff --git a/bacnet-stack/ports/win32/bip.c b/bacnet-stack/ports/win32/bip.c new file mode 100644 index 00000000..0d0065c1 --- /dev/null +++ b/bacnet-stack/ports/win32/bip.c @@ -0,0 +1,345 @@ +/*####COPYRIGHTBEGIN#### + ------------------------------------------- + Copyright (C) 2005 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####*/ + +#define WIN32_LEAN_AND_MEAN +#define STRICT + +#include +#include +#include +#include // for standard integer types uint8_t etc. +#include // for the standard bool type. +#include "bacdcode.h" +#include "bip.h" + +#define close closesocket + +static int BIP_Socket = -1; +/* port to use - stored in network byte order */ +static uint16_t BIP_Port = 0; +/* IP Address - stored in network byte order */ +static struct in_addr BIP_Address; +/* Broadcast Address */ +static struct in_addr BIP_Broadcast_Address; +/* Subnet Mask */ +static struct in_addr BIP_Subnet_Mask; + +bool bip_valid(void) +{ + return (BIP_Socket != -1); +} + +void bip_cleanup(void) +{ + if (bip_valid()) + close(BIP_Socket); + BIP_Socket = -1; + + return; +} + +static void set_network_address(struct in_addr *net_address, + uint8_t octet1, uint8_t octet2, uint8_t octet3, uint8_t octet4) +{ + union { + uint8_t byte[4]; + uint32_t value; + } long_data = {{0}}; + + long_data.byte[0] = octet1; + long_data.byte[1] = octet2; + long_data.byte[2] = octet3; + long_data.byte[3] = octet4; + + net_address->s_addr = htonl(long_data.value); +} + +void bip_set_address(uint8_t octet1, uint8_t octet2, + uint8_t octet3, uint8_t octet4) +{ + set_network_address(&BIP_Address, octet1, octet2, octet3, octet4); +} + +void bip_set_broadcast_address(uint8_t octet1, uint8_t octet2, + uint8_t octet3, uint8_t octet4) +{ + set_network_address(&BIP_Broadcast_Address, + octet1, octet2, octet3, octet4); +} + +void bip_set_subnet_mask(uint8_t octet1, uint8_t octet2, + uint8_t octet3, uint8_t octet4) +{ + set_network_address(&BIP_Subnet_Mask, + octet1, octet2, octet3, octet4); +} + +void bip_set_port(uint16_t port) +{ + BIP_Port = htons(port); +} + +bool bip_init(void) +{ + int rv = 0; // return from socket lib calls + struct sockaddr_in sin = {-1}; + int value = 1; + + /* local broadcast address */ + //BIP_Broadcast_Address.s_addr = BIP_Address.s_addr; + //BIP_Broadcast_Address.s_addr |= ~(BIP_Subnet_Mask.s_addr); + BIP_Broadcast_Address.s_addr = INADDR_BROADCAST; + /* configure standard BACnet/IP port */ + bip_set_port(0xBAC0); + + // assumes that the driver has already been initialized + BIP_Socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (BIP_Socket < 0) + return false; + + // bind the socket to the local port number and IP address + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = BIP_Port; + memset(&(sin.sin_zero), '\0', 8); + rv = bind(BIP_Socket, + (const struct sockaddr*)&sin, sizeof(struct sockaddr)); + if (rv < 0) + { + close(BIP_Socket); + BIP_Socket = -1; + return false; + } + + return true; +} + +/* function to send a packet out the BACnet/IP socket (Annex J) */ +/* returns number of bytes sent on success, negative number on failure */ +static int bip_send( + struct sockaddr_in *bip_dest, + uint8_t *pdu, // any data to be sent - may be null + unsigned pdu_len) // number of bytes of data +{ + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + // assumes that the driver has already been initialized + if (BIP_Socket < 0) + return BIP_Socket; + + mtu[0] = 0x81; /* BVLL for BACnet/IP */ + if (bip_dest->sin_addr.s_addr == BIP_Broadcast_Address.s_addr) + mtu[1] = 0x0B; /* Original-Broadcast-NPDU */ + else + mtu[1] = 0x0A; /* Original-Unicast-NPDU */ + mtu_len = 2; + mtu_len += encode_unsigned16(&mtu[mtu_len], pdu_len); + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + + /* Send the packet */ + bytes = sendto(BIP_Socket, (char *)mtu, mtu_len, 0, + (struct sockaddr *)bip_dest, + sizeof(struct sockaddr)); + + return bytes; +} + +/* function to send a packet out the BACnet/IP socket (Annex J) */ +/* returns number of bytes sent on success, negative number on failure */ +int bip_send_pdu( + BACNET_ADDRESS *dest, // destination address + uint8_t *pdu, // any data to be sent - may be null + unsigned pdu_len) // number of bytes of data +{ + int i = 0; // counter + struct sockaddr_in bip_dest; + uint32_t network_address = 0; + uint16_t network_port = 0; + + /* load destination IP address */ + bip_dest.sin_family = AF_INET; + if (dest->mac_len == 6) + { + (void)decode_unsigned32(&dest->mac[0], &(bip_dest.sin_addr.s_addr)); + (void)decode_unsigned16(&dest->mac[4], &(bip_dest.sin_port)); + memset(&(bip_dest.sin_zero), '\0', 8); + } + /* broadcast */ + else if (dest->mac_len == 0) + { + bip_dest.sin_addr.s_addr = BIP_Broadcast_Address.s_addr; + bip_dest.sin_port = BIP_Port; + memset(&(bip_dest.sin_zero), '\0', 8); + } + else + return -1; + + /* function to send a packet out the BACnet/IP socket */ + /* returns 1 on success, 0 on failure */ + return bip_send(&bip_dest, // destination address + pdu, // any data to be sent - may be null + pdu_len); // number of bytes of data +} + +// receives a BACnet/IP packet +// returns the number of octets in the PDU, or zero on failure +uint16_t bip_receive( + BACNET_ADDRESS *src, // source address + uint8_t *pdu, // PDU data + uint16_t max_pdu, // amount of space available in the PDU + unsigned timeout) // number of milliseconds to wait for a packet +{ + int received_bytes; + uint8_t buf[MAX_MPDU] = {0}; // data + uint16_t pdu_len = 0; // return value + fd_set read_fds; + int max; + struct timeval select_timeout; + struct sockaddr_in sin = {-1}; + int sin_len = sizeof(sin); + + /* Make sure the socket is open */ + if (BIP_Socket < 0) + return 0; + + /* we could just use a non-blocking socket, but that consumes all + the CPU time. We can use a timeout; it is only supported as + a select. */ + if (timeout >= 1000) + { + select_timeout.tv_sec = timeout / 1000; + select_timeout.tv_usec = + 1000 * (timeout - select_timeout.tv_sec * 1000); + } + else + { + select_timeout.tv_sec = 0; + select_timeout.tv_usec = 1000 * timeout; + } + FD_ZERO(&read_fds); + FD_SET(BIP_Socket, &read_fds); + max = BIP_Socket; + /* see if there is a packet for us */ + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = recvfrom(BIP_Socket, + (char *)&buf[0], MAX_MPDU, 0, + (struct sockaddr *)&sin, &sin_len); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + return 0; + } + + /* no problem, just no bytes */ + if (received_bytes == 0) + return 0; + + /* the signature of a BACnet/IP packet */ + if (buf[0] != 0x81) + return 0; + /* Original-Broadcast-NPDU or Original-Unicast-NPDU */ + if ((buf[1] == 0x0B) || (buf[1] == 0x0A)) + { + // copy the source address + src->mac_len = 6; + (void)encode_unsigned32(&src->mac[0], + sin.sin_addr.s_addr); + (void)encode_unsigned16(&src->mac[4], + sin.sin_port); + // FIXME: check destination address + // see if it is broadcast or for us + /* decode the length of the PDU - length is inclusive of BVLC */ + (void)decode_unsigned16(&buf[2],&pdu_len); + /* copy the buffer into the PDU */ + pdu_len -= 4; /* BVLC header */ + if (pdu_len < max_pdu) + memmove(&pdu[0],&buf[4],pdu_len); + // ignore packets that are too large + // clients should check my max-apdu first + else + pdu_len = 0; + } + + return pdu_len; +} + +void bip_get_my_address(BACNET_ADDRESS *my_address) +{ + int i = 0; + + my_address->mac_len = 6; + (void)encode_unsigned32(&my_address->mac[0], + BIP_Address.s_addr); + (void)encode_unsigned16(&my_address->mac[4], + BIP_Port); + my_address->net = 0; /* local only, no routing */ + my_address->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) + { + /* no SADR */ + my_address->adr[i] = 0; + } + + return; +} + +void bip_get_broadcast_address( + BACNET_ADDRESS *dest) // destination address +{ + int i = 0; // counter + + if (dest) + { + dest->mac_len = 6; + (void)encode_unsigned32(&dest->mac[0], + BIP_Broadcast_Address.s_addr); + (void)encode_unsigned16(&dest->mac[4], + BIP_Port); + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; /* no SLEN */ + for (i = 0; i < MAX_MAC_LEN; i++) + { + /* no SADR */ + dest->adr[i] = 0; + } + } + + return; +} diff --git a/bacnet-stack/ports/win32/main.c b/bacnet-stack/ports/win32/main.c new file mode 100644 index 00000000..e7ec2b09 --- /dev/null +++ b/bacnet-stack/ports/win32/main.c @@ -0,0 +1,145 @@ +/************************************************************************** +* +* Copyright (C) 2005 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. +* +*********************************************************************/ + +// This is one way to use the embedded BACnet stack under Win32 +// compiled with Borland C++ 5.02 +#include +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "handlers.h" +#include "bip.h" + +// buffer used for receive +static uint8_t Rx_Buf[MAX_MPDU] = {0}; + +static BYTE TargetIP[] = {192, 168, 0, 191}; +static BYTE NetMask[] = {255, 255, 255, 0}; + +static void Init_Device_Parameters(void) +{ + // configure my initial values + Device_Set_Object_Instance_Number(126); + Device_Set_Vendor_Name("Lithonia Lighting"); + Device_Set_Vendor_Identifier(42); + Device_Set_Model_Name("Simple BACnet Server"); + Device_Set_Firmware_Revision("1.00"); + Device_Set_Application_Software_Version("none"); + Device_Set_Description("Example of a simple BACnet server"); + + return; +} + +static void Init_Service_Handlers(void) +{ + // we need to handle who-is to support dynamic device binding + apdu_set_unconfirmed_handler( + SERVICE_UNCONFIRMED_WHO_IS, + WhoIsHandler); + // set the handler for all the services we don't implement + // It is required to send the proper reject message... + apdu_set_unrecognized_service_handler_handler( + UnrecognizedServiceHandler); + // we must implement read property - it's required! + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_READ_PROPERTY, + ReadPropertyHandler); + apdu_set_confirmed_handler( + SERVICE_CONFIRMED_WRITE_PROPERTY, + WritePropertyHandler); +} + +static void NetInitialize(void) +// initialize the TCP/IP stack +{ + int Result; + int Code; + WSADATA wd; + + Result = WSAStartup(MAKEWORD(2,2), &wd); + + if (Result != 0) + { + Code = WSAGetLastError(); + printf("TCP/IP stack initialization failed, error code: %i\n", + Code); + exit(1); + } +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = {0}; // address where message came from + uint16_t pdu_len = 0; + unsigned timeout = 100; // milliseconds + + (void)argc; + (void)argv; + Init_Device_Parameters(); + Init_Service_Handlers(); + // init the data link layer + NetInitialize(); + bip_set_address(TargetIP[0], TargetIP[1], TargetIP[2], TargetIP[3]); + bip_set_address(NetMask[0], NetMask[1], NetMask[2], NetMask[3]); + if (!bip_init()) + return 1; + + printf("BACnet stack running...\n"); + // loop forever + for (;;) + { + // input + + // returns 0 bytes on timeout + pdu_len = bip_receive( + &src, + &Rx_Buf[0], + MAX_MPDU, + timeout); + + // process + + if (pdu_len) + { + npdu_handler( + &src, + &Rx_Buf[0], + pdu_len); + } + if (I_Am_Request) + { + I_Am_Request = false; + Send_IAm(); + } + // output + + // blink LEDs, Turn on or off outputs, etc + } +} diff --git a/bacnet-stack/ports/win32/stdbool.h b/bacnet-stack/ports/win32/stdbool.h new file mode 100644 index 00000000..29b9a5e4 --- /dev/null +++ b/bacnet-stack/ports/win32/stdbool.h @@ -0,0 +1,28 @@ +#ifndef STDBOOL_H +#define STDBOOL_H + +// C99 Boolean types for compilers without C99 support + +#ifndef __cplusplus + typedef int _Bool; + #ifndef bool + #define bool _Bool + #endif + #ifndef true + #define true 1 + #endif + #ifndef false + #define false 0 + #endif + #define __bool_true_false_are_defined 1 +#endif + +#ifndef FALSE + #define FALSE 0 +#endif + +#ifndef TRUE + #define TRUE 1 +#endif + +#endif diff --git a/bacnet-stack/ports/win32/stdint.h b/bacnet-stack/ports/win32/stdint.h new file mode 100644 index 00000000..3615d7ea --- /dev/null +++ b/bacnet-stack/ports/win32/stdint.h @@ -0,0 +1,19 @@ +// 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; + +#endif // STDINT_H