diff --git a/bacnet-stack/ports/rtos32/ethernet.c b/bacnet-stack/ports/rtos32/ethernet.c new file mode 100644 index 00000000..61dc7e29 --- /dev/null +++ b/bacnet-stack/ports/rtos32/ethernet.c @@ -0,0 +1,396 @@ +/************************************************************************** +* +* 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. +* +*********************************************************************/ + +#include // for standard integer types uint8_t etc. +#include // for the standard bool type. +// commonly used comparison address for ethernet +uint8_t Ethernet_Broadcast[MAX_MAC_LEN] = + { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; +// commonly used empty address for ethernet quick compare +uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; +// my local device data - MAC address +uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 }; + +// static IP address assignment (default) +static BYTE TargetIP[] = {192, 168, 0, 50}; +// net mask - set to be subnet restrictive +static BYTE NetMask[] = {255, 255, 255, 0}; +// gateway - set to zero if not available or required +static BYTE DefaultGateway[] = {0, 0, 0, 0}; +// DNS - set to zero if not available or required +static BYTE DNSServer[] = {0, 0, 0, 0}; +// the actual socket for Ethernet +// SOCKET_ERROR means no open interface +static int Ethernet_Interface = SOCKET_ERROR; + +bool ethernet_valid(void) +{ + return (Ethernet_Interface != SOCKET_ERROR); +} + +void ethernet_cleanup(void) +{ + if (ethernet_valid()) + xn_interface_close(Ethernet_Interface); + Ethernet_Interface = SOCKET_ERROR; + + return; +} + +/* function to find the local ethernet MAC address */ +static int get_local_hwaddr(int iface, unsigned char *mac) +{ + struct _iface_info ii; // contains the hwaddr of the Ethernet interface + + /* determine the local MAC address */ + xn_interface_info(iface, &ii); + mac[0] = ii.my_ethernet_address[0]; + mac[1] = ii.my_ethernet_address[1]; + mac[2] = ii.my_ethernet_address[2]; + mac[3] = ii.my_ethernet_address[3]; + mac[4] = ii.my_ethernet_address[4]; + mac[5] = ii.my_ethernet_address[5]; + + return rv; +} + +static void ethernet_error(const char * Msg) +{ + printf("%s, error code: %s\n", Msg, xn_geterror_string(WSAGetLastError())); + exit(1); +} + +bool ethernet_init(char *interface_name) +{ + struct _iface_info ii; // contains the hwaddr of the Ethernet interface + + // FIXME: what about other drivers other than DAVICOM? + (void)interface_name; + RTKernelInit(0); // get the kernel going + if (!RTKDebugVersion()) // switch of all diagnostics and error messages of RTIP-32 + xn_callbacks()->cb_wr_screen_string_fnc = NULL; + + CLKSetTimerIntVal(10*1000); // 10 millisecond tick + RTKDelay(1); + RTCMOSSetSystemTime(); // get the right time-of-day + + Ethernet_Interface = xn_rtip_init(); // Initialize the RTIP stack + if (Ethernet_Interface == SOCKET_ERROR) + ethernet_error("xn_rtip_init failed"); + atexit(ethernet_cleanup); // make sure the driver is shut down properly + RTCallDebugger(RT_DBG_CALLRESET, (DWORD)exit, 0); // even if we get restarted by the debugger + + // PCI device ignores the IRQ and IO parameters + Ethernet_Interface = xn_interface_open_config( + DAVICOM_DEVICE, MINOR_0, 0, 0, 0); + if (Ethernet_Interface == SOCKET_ERROR) + { + fprintf(stderr,"ethernet: Davicom driver failed to initialize\r\n"); + return false; + } + xn_interface_info(Ethernet_Interface, &ii); + printf("ethernet: MAC address: %02x-%02x-%02x-%02x-%02x-%02x\n", + ii.my_ethernet_address[0], ii.my_ethernet_address[1], + ii.my_ethernet_address[2], ii.my_ethernet_address[3], + ii.my_ethernet_address[4], ii.my_ethernet_address[5]); + // Set the IP address and interface + printf("ethernet: static IP address %i.%i.%i.%i\n", + TargetIP[0], TargetIP[1], TargetIP[2], TargetIP[3]); + if (xn_set_ip(Ethernet_Interface, TargetIP, NetMask) == SOCKET_ERROR) + { + // FIXME: is this because of a duplicate address? Tell user... + fprintf(stderr,"ethernet: failed to set IP address!\r\n"); + return false; + } + // add a route in the routing table + // ip_ffaddr is apparently some global... + xn_rt_add(RT_DEFAULT, ip_ffaddr, DefaultGateway, 1, + Ethernet_Interface, RT_INF); + xn_set_server_list((DWORD*)DNSServer, 1); + + return ethernet_valid(); +} + +/* function to send a packet out the 802.2 socket */ +/* returns 0 on success, non-zero on failure */ +int ethernet_send( + BACNET_ADDRESS *dest, // destination address + BACNET_ADDRESS *src, // source address + uint8_t *pdu, // any data to be sent - may be null + unsigned pdu_len) // number of bytes of data +{ + int status = -1; + int bytes = 0; + uint8_t mtu[MAX_MPDU] = { 0 }; + int mtu_len = 0; + int i = 0; + + // don't waste time if the socket is not valid + if (Ethernet_Interface < 0) + { + fprintf(stderr, "ethernet: 802.2 socket is invalid!\n"); + return status; + } + /* load destination ethernet MAC address */ + if (dest->mac_len == 6) + { + for (i = 0; i < 6; i++) + { + mtu[mtu_len] = dest->mac[i]; + mtu_len++; + } + } + else + { + fprintf(stderr, "ethernet: invalid destination MAC address!\n"); + return status; + } + + /* load source ethernet MAC address */ + if (src->mac_len == 6) + { + for (i = 0; i < 6; i++) + { + mtu[mtu_len] = src->mac[i]; + mtu_len++; + } + } + else + { + fprintf(stderr, "ethernet: invalid source MAC address!\n"); + return status; + } + if ((14 + 3 + pdu_len) > MAX_MPDU) + { + fprintf(stderr, "ethernet: PDU is too big to send!\n"); + return status; + } + /* packet length */ + mtu_len += encode_unsigned16(&mtu[12], + 3 /*DSAP,SSAP,LLC*/ + pdu_len); + // Logical PDU portion + mtu[mtu_len++] = 0x82; /* DSAP for BACnet */ + mtu[mtu_len++] = 0x82; /* SSAP for BACnet */ + mtu[mtu_len++] = 0x03; /* Control byte in header */ + memcpy(&mtu[mtu_len], pdu, pdu_len); + mtu_len += pdu_len; + + /* Send the packet */ + bytes = + sendto(Ethernet_Interface, &mtu, mtu_len, 0, + (struct sockaddr *) ð_addr, sizeof(struct sockaddr)); + /* did it get sent? */ + if (bytes < 0) { + fprintf(stderr,"ethernet: Error sending packet: %s\n", + strerror(errno)); + return status; + } + + // got this far - must be good! + status = 0; + + return status; +} + +/* function to send a packet out the 802.2 socket */ +/* returns zero on success, non-zero on failure */ +int ethernet_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 + BACNET_ADDRESS src = {0}; // source address + + for (i = 0; i < 6; i++) + { + src.mac[i] = Ethernet_MAC_Address[i]; + src.mac_len++; + } + /* function to send a packet out the 802.2 socket */ + /* returns 1 on success, 0 on failure */ + return ethernet_send(dest, // destination address + &src, // source address + pdu, // any data to be sent - may be null + pdu_len); // number of bytes of data +} + +// receives an 802.2 framed packet +// returns the number of octets in the PDU, or zero on failure +uint16_t ethernet_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; + + /* Make sure the socket is open */ + if (Ethernet_Interface <= 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(Ethernet_Interface, &read_fds); + max = Ethernet_Interface; + + if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) + received_bytes = read(Ethernet_Interface, &buf[0], MAX_MPDU); + else + return 0; + + /* See if there is a problem */ + if (received_bytes < 0) { + // EAGAIN Non-blocking I/O has been selected + // using O_NONBLOCK and no data + // was immediately available for reading. + if (errno != EAGAIN) + fprintf(stderr,"ethernet: Read error in receiving packet: %s\n", + strerror(errno)); + return 0; + } + + if (received_bytes == 0) + return 0; + + /* the signature of an 802.2 BACnet packet */ + if ((buf[14] != 0x82) && (buf[15] != 0x82)) { + //fprintf(stderr,"ethernet: Non-BACnet packet\n"); + return 0; + } + // copy the source address + src->mac_len = 6; + memmove(src->mac, &buf[6], 6); + + // check destination address for when + // the Ethernet card is in promiscious mode + if ((memcmp(&buf[0], Ethernet_MAC_Address,6) != 0) + && (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) + { + //fprintf(stderr, "ethernet: This packet isn't for us\n"); + return 0; + } + + (void)decode_unsigned16(&buf[12],&pdu_len); + pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ; + // copy the buffer into the PDU + if (pdu_len < max_pdu) + memmove(&pdu[0],&buf[17],pdu_len); + + return pdu_len; +} + +void ethernet_get_my_address(BACNET_ADDRESS *my_address) +{ + int i = 0; + + my_address->mac_len = 0; + for (i = 0; i < 6; i++) + { + my_address->mac[i] = Ethernet_MAC_Address[i]; + my_address->mac_len++; + } + 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 ethernet_set_broadcast_address( + BACNET_ADDRESS *dest) // destination address +{ + int i = 0; // counter + + if (dest) + { + for (i = 0; i < 6; i++) + { + dest->mac[i] = Ethernet_Broadcast[i]; + } + dest->mac_len = 6; + dest->net = BACNET_BROADCAST_NETWORK; + dest->len = 0; // denotes broadcast address + for (i = 0; i < MAX_MAC_LEN; i++) + { + dest->adr[i] = 0; + } + } + + return; +} + +void ethernet_debug_address( + const char *info, + BACNET_ADDRESS *dest) +{ + int i = 0; // counter + + if (info) + fprintf(stderr,"%s",info); + if (dest) + { + fprintf(stderr,"Address:\n"); + fprintf(stderr," MAC Length=%d\n",dest->mac_len); + fprintf(stderr," MAC Address="); + for (i = 0; i < MAX_MAC_LEN; i++) + { + fprintf(stderr,"%02X ",(unsigned)dest->mac[i]); + } + fprintf(stderr,"\n"); + fprintf(stderr," Net=%hu\n",dest->net); + fprintf(stderr," Len=%d\n",dest->len); + fprintf(stderr," Adr="); + for (i = 0; i < MAX_MAC_LEN; i++) + { + fprintf(stderr,"%02X ",(unsigned)dest->adr[i]); + } + fprintf(stderr,"\n"); + } + + return; +} + diff --git a/bacnet-stack/ports/rtos32/hardware.cfg b/bacnet-stack/ports/rtos32/hardware.cfg new file mode 100644 index 00000000..f85c1eb9 --- /dev/null +++ b/bacnet-stack/ports/rtos32/hardware.cfg @@ -0,0 +1,24 @@ +// * The target computer is IBM-PC-AT compatible. +// * There is a minimum of 4 MB of RAM installed. + +#ifdef DEBUGDOS + Region RealModeVectors 0 1k RAM NoAccess // interrupt vectors + Region BIOSDataArea 1k 3k RAM ReadOnly // BIOS data area + Region DOSMem 4k 252k RAM + Region LowMem 256k 256k RAM +#else + Region = RealModeVectors 0, 4k, RAM, NoAccess // interrupt vectors +// note: locate only has 4k granularity so the 0-1k will be readonly +// Region = RealModeVectors 0, 1k, RAM, NoAccess // interrupt vectors +// Region = BIOSDataArea 1k, 3k, RAM, ReadOnly // BIOS data area + Region = LowMem 4k, 508k, RAM, Assign // Conventional memory +#endif +Region = MoreLowMem 512k, 128k, RAM, Assign // Reserved BIOS ext +Region = MonoText B0000h 4k, Device, ReadWrite // Mono text video mem +Region = ColorText B8000h, 4k, Device, ReadWrite // Text mode video ram +Region = DiskOnChip D0000h, 8k, Device, ReadWrite // Driver Ampro Card +Region = DiskOnChip1 E8000h, 32k, Device, ReadWrite // Driver WinSys Card +Region = HighMem 1M, 3M, RAM, Assign // 3mb ext mem on target +Virtual HeapMem 1G +Virtual StackMem 2G +Virtual ProgMem 3G diff --git a/bacnet-stack/ports/rtos32/init.c b/bacnet-stack/ports/rtos32/init.c new file mode 100644 index 00000000..65cc0ef3 --- /dev/null +++ b/bacnet-stack/ports/rtos32/init.c @@ -0,0 +1,128 @@ +/************************************************************************** +* +* 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. +* +*********************************************************************/ + +#include +#include +#include + +extern void RTEmuInit(void); + +#ifdef _MSC_VER + #define VOIDEXPORT _declspec(dllexport) void __cdecl +#else + #define VOIDEXPORT void __export __cdecl +#endif + +/* DISK SYSTEM */ +#ifdef DOC // include DiskOnChip driver + #include + #define RTF_MAX_FILES 16 // support for more open files (default is 8) + #define RTF_BUFFERS_IN_BSS // we do not need file I/O before the run-time + #include // system is initialized + + //#define READ_HEAD_BUFFER_SIZE 2048+4 + + //static BYTE ReadAheadBuffer[READ_HEAD_BUFFER_SIZE]; + + static RTFDrvFLPYData FLPYDriveAData = {0}; + static RTFDrvDOCData DOCDriveData = {0}; + static RTFDrvIDEData IDEDriveData = {0}; + + RTFDevice RTFDeviceList[] = + { + /* type,number,flags,driver,driverdata */ + { RTF_DEVICE_FLOPPY, 0, 0, &RTFDrvFloppy, &FLPYDriveAData}, + { RTF_DEVICE_FDISK , 0, 0, &RTFDrvDOC, &DOCDriveData}, + { RTF_DEVICE_FDISK , 0, 0, &RTFDrvIDE, &IDEDriveData}, + { 0 , 0, 0, NULL, NULL} + }; +#endif +/* END OF DISK SYSTEM */ + +/* RTTarget only defines 64 Win32 handles, which are not enough for BACstac */ +/* the following code is from the RTTarget manual, page 106 */ +#define MAXHANDLES 1024 +#define MAXOBJECTS 1024 +#define MAXTYPES 32 + +RTW32Handle RTHandleTable[MAXHANDLES] = {{0}}; +int RTHandleCount = MAXHANDLES; + +RTW32Object RTObjectTable[MAXOBJECTS] = {{0}}; +int RTObjectCount = MAXOBJECTS; + +RTW32Types RTTypeTable[MAXTYPES] = {{0}}; +int RTTypeCount = MAXTYPES; + +#if 0 +/* We can embed some files in the RTB file, like a binary + file used for configuring a remote device, using + 'Locate File filename HighMem' in the config (.CFG) file. + However, the default setup for RTFiles and RTTarget + doesn't include the RAM files, so we need to specify + that here, as well as the LPT, console, and FAT. + From RTFiles-32 manual, ch. 7, "Using RTFiles-32 with + RTTarget-32" */ +RTFileSystem Console = +{ RT_FS_CONSOLE, 0, 0, &RTConsoleFileSystem }; + +RTFileSystem LPTFiles = +{ RT_FS_LPT_DEVICE, 0, 0, &RTLPTFileSystem }; + +/* logical drive Z: can be used to access the RAM drive */ +RTFileSystem RAMFiles = +{ RT_FS_FILE,1 << ('Z'-'A'), 0, &RTRAMFileSystem }; + +/* logical drive A: through D: are reserved for FAT */ +RTFileSystem FATFiles = +{ RT_FS_FILE | RT_FS_IS_DEFAULT, 0x0F, 0x03, &RTFilesFileSystem }; + +RTFileSystem *RTFileSystemList[] = +{ + &Console, + &LPTFiles, + &RAMFiles, + &FATFiles, + NULL, +}; +#endif + +/*-----------------------------------*/ +VOIDEXPORT Init(void) +{ + (void)RTSetFlags(RT_MM_VIRTUAL, 1); // this is the better method + (void)RTCMOSExtendHeap(); // get as much memory as we can + RTCMOSSetSystemTime(); // get the right date and time + RTEmuInit(); // set up floating point emulation + // pizza - RTHaltCPL3 appears to cause problems with file handling + //RTIdleHandler = (void RTTAPI *)RTHaltCPL3; // low power when idle + // not needed with pre-emptive + //RTKTimeSlice(2); // allow same priority task switch + RTKConfig.Flags |= RF_PREEMPTIVE; // preemptive multitasking + RTKConfig.Flags |= RF_WIN32MUTEX_MUTEX; // Win32 mutexes are RTK32 mutexes + RTKConfig.Flags |= RF_FPCONTEXT; // saves floating point context for tasks + RTKConfig.HookedIRQs |= 1 << 1; // hook the keyboard IRQ + RTKConfig.DefaultTaskStackSize = 1024*8; // for Win32 task stacks req = 0 +} diff --git a/bacnet-stack/ports/rtos32/main.c b/bacnet-stack/ports/rtos32/main.c new file mode 100644 index 00000000..a3ced3b9 --- /dev/null +++ b/bacnet-stack/ports/rtos32/main.c @@ -0,0 +1,340 @@ +/************************************************************************** +* +* 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 RTOS-32 +// compiled with Borland C++ 5.02 + +#include +#include +#include +#include "config.h" +#include "bacdef.h" +#include "npdu.h" +#include "apdu.h" +#include "device.h" +#include "rp.h" +#include "iam.h" +#include "whois.h" +#include "reject.h" +#include "abort.h" +#include "bacerror.h" +#include "ethernet.h" + +// buffers used for transmit and receive +static uint8_t Tx_Buf[MAX_MPDU] = {0}; +static uint8_t Rx_Buf[MAX_MPDU] = {0}; +static uint8_t Temp_Buf[MAX_APDU] = {0}; + +// flag to send an I-Am +bool I_Am_Request = true; + +void UnrecognizedServiceHandler( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *dest, + BACNET_CONFIRMED_SERVICE_DATA *service_data) +{ + BACNET_ADDRESS src; + int pdu_len = 0; + + ethernet_get_my_address(&src); + + // encode the NPDU portion of the packet + pdu_len = npdu_encode_apdu( + &Tx_Buf[0], + dest, + &src, + false, // true for confirmed messages + MESSAGE_PRIORITY_NORMAL); + + // encode the APDU portion of the packet + pdu_len += reject_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + REJECT_REASON_UNRECOGNIZED_SERVICE); + + (void)ethernet_send_pdu( + dest, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent Reject!\n"); +} + +// FIXME: if we handle multiple ports, then a port neutral version +// of this would be nice. Then it could be moved into iam.c +void Send_IAm(void) +{ + int pdu_len = 0; + BACNET_ADDRESS dest; + + // I-Am is a global broadcast + ethernet_set_broadcast_address(&dest); + + // encode the NPDU portion of the packet + pdu_len = npdu_encode_apdu( + &Tx_Buf[0], + &dest, + NULL, + false, // true for confirmed messages + MESSAGE_PRIORITY_NORMAL); + + // encode the APDU portion of the packet + pdu_len += iam_encode_apdu( + &Tx_Buf[pdu_len], + Device_Object_Instance_Number(), + MAX_APDU, + SEGMENTATION_NONE, + Device_Vendor_Identifier()); + + (void)ethernet_send_pdu( + &dest, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent I-Am Request!\n"); +} + +void WhoIsHandler( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src) +{ + int len = 0; + int32_t low_limit = 0; + int32_t high_limit = 0; + + fprintf(stderr,"Received Who-Is Request!\n"); + len = whois_decode_service_request( + service_request, + service_len, + &low_limit, + &high_limit); + if (len == 0) + I_Am_Request = true; + else if (len != -1) + { + if ((Device_Object_Instance_Number() >= low_limit) && + (Device_Object_Instance_Number() <= high_limit)) + I_Am_Request = true; + } + + return; +} + +void ReadPropertyHandler( + uint8_t *service_request, + uint16_t service_len, + BACNET_ADDRESS *src, + BACNET_CONFIRMED_SERVICE_DATA *service_data) +{ + BACNET_READ_PROPERTY_DATA rp_data; + int len = 0; + int pdu_len = 0; + BACNET_OBJECT_TYPE object_type; + uint32_t object_instance; + BACNET_PROPERTY_ID object_property; + int32_t array_index; + BACNET_ADDRESS my_address; + + fprintf(stderr,"Received Read-Property Request!\n"); + len = rp_decode_service_request( + service_request, + service_len, + &object_type, + &object_instance, + &object_property, + &array_index); + // prepare a reply + ethernet_get_my_address(&my_address); + // encode the NPDU portion of the packet + pdu_len = npdu_encode_apdu( + &Tx_Buf[0], + src, + &my_address, + false, // true for confirmed messages + MESSAGE_PRIORITY_NORMAL); + // bad encoding - send an abort + if (len == -1) + { + pdu_len += abort_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + ABORT_REASON_OTHER); + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent Abort!\n"); + } + else if (service_data->segmented_message) + { + pdu_len += abort_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + ABORT_REASON_SEGMENTATION_NOT_SUPPORTED); + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + } + else + { + switch (object_type) + { + case OBJECT_DEVICE: + // FIXME: probably need a length limitation sent with encode + // FIXME: might need to return error codes + len = Device_Encode_Property_APDU( + &Temp_Buf[0], + object_property, + array_index); + if (len > 0) + { + // encode the APDU portion of the packet + rp_data.object_type = object_type; + rp_data.object_instance = object_instance; + rp_data.object_property = object_property; + rp_data.array_index = array_index; + rp_data.application_data = &Temp_Buf[0]; + rp_data.application_data_len = len; + // FIXME: probably need a length limitation sent with encode + pdu_len += rp_ack_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + &rp_data); + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent Read Property Ack!\n"); + } + else + { + pdu_len += bacerror_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, + ERROR_CLASS_PROPERTY, + ERROR_CODE_UNKNOWN_PROPERTY); + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent Unknown Property Error!\n"); + } + break; + default: + pdu_len += bacerror_encode_apdu( + &Tx_Buf[pdu_len], + service_data->invoke_id, + SERVICE_CONFIRMED_READ_PROPERTY, + ERROR_CLASS_OBJECT, + ERROR_CODE_UNKNOWN_OBJECT); + (void)ethernet_send_pdu( + src, // destination address + &Tx_Buf[0], + pdu_len); // number of bytes of data + fprintf(stderr,"Sent Unknown Object Error!\n"); + break; + } + } + + return; +} + +static void Init_Device_Parameters(void) +{ + // configure my initial values + Device_Set_Object_Instance_Number(111); + 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); +} + +int main(int argc, char *argv[]) +{ + BACNET_ADDRESS src = {0}; // address where message came from + uint16_t pdu_len = 0; + unsigned timeout = 100; // milliseconds + + Init_Device_Parameters(); + Init_Service_Handlers(); + // init the physical layer + if (!ethernet_init("eth0")) + return 1; + + // loop forever + for (;;) + { + // input + + // returns 0 bytes on timeout + pdu_len = ethernet_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 + } + + return 0; +} diff --git a/bacnet-stack/ports/rtos32/makefile.mak b/bacnet-stack/ports/rtos32/makefile.mak new file mode 100644 index 00000000..58454571 --- /dev/null +++ b/bacnet-stack/ports/rtos32/makefile.mak @@ -0,0 +1,153 @@ +# +# Simple makefile to build an RTB executable for RTOS-32 +# +# This makefile assumes Borland bcc32 development environment +# on Windows NT/9x/2000/XP +# + +!ifndef RTOS32_DIR +RTOS32_DIR_Not_Defined: + @echo . + @echo You must define environment variable RTOS32_DIR to for this build. +!endif + +!ifndef BORLAND_DIR +BORLAND_DIR_Not_Defined: + @echo . + @echo You must define environment variable BORLAND_DIR to compile +!endif + +PRODUCT = bacnet +PRODUCT_RTB = $(PRODUCT).rtb +PRODUCT_EXE = $(PRODUCT).exe + +SRCS = init.c main.c ethernet.c \ + ..\..\bacdcode.c \ + ..\..\bigend.c \ + ..\..\whois.c \ + ..\..\iam.c \ + ..\..\rp.c \ + ..\..\device.c \ + ..\..\abort.c \ + ..\..\reject.c \ + ..\..\bacerror.c \ + ..\..\apdu.c \ + ..\..\npdu.c + +OBJS = $(SRCS:.c=.obj) + +# Compiler definitions +# +CC = $(BORLAND_DIR)\bin\bcc32 +bcc32.cfg +LINK = $(BORLAND_DIR)\bin\tlink32 +TLIB = $(BORLAND_DIR)\bin\tlib +LOCATE = $(RTOS32_DIR)\bin\rtloc + +# +# Include directories +# +CC_DIR = $(BORLAND_DIR)\BIN +CC_INCLDIR = $(BORLAND_DIR)\include +INCL_DIRS = -I$(BORLAND_DIR)\include;$(RTOS32_DIR)\include +DEFINES = -DDOC + +CFLAGS = $(INCL_DIRS) $(CS_FLAGS) $(DEFINES) + +# Libraries +# +RTOS32_LIB_DIR = $(RTOS32_DIR)\libbc +C_LIB_DIR = $(BORLAND_DIR)\lib + +LIBDIR = $(RTOS32_LIB_DIR);$(C_LIB_DIR) + +LIBS = $(RTOS32_LIB_DIR)\RTFILES.LIB \ +$(RTOS32_LIB_DIR)\RTFSK32.LIB \ +$(RTOS32_LIB_DIR)\DRVDOC.LIB \ +$(RTOS32_LIB_DIR)\RTIP.LIB \ +$(RTOS32_LIB_DIR)\RTK32.LIB \ +$(RTOS32_LIB_DIR)\FLTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\DRVRT32.LIB \ +$(RTOS32_LIB_DIR)\RTEMUMT.LIB \ +$(RTOS32_LIB_DIR)\RTT32.LIB \ +$(RTOS32_LIB_DIR)\RTTHEAP.LIB \ +$(C_LIB_DIR)\DPMI32.lib \ +$(C_LIB_DIR)\IMPORT32.lib \ +$(C_LIB_DIR)\CW32MT.lib + +# +# Main target +# +# This should be the first one in the makefile + +all : $(PRODUCT_RTB) monitor.rtb + +monitor.rtb: monitor.cfg hardware.cfg + $(LOCATE) monitor + +# debug using COM3 (ISA Card) as the debug port +# boot from floppy +debugcom3: hardware.cfg software.cfg $(PRODUCT_RTB) monitor.rtb + $(LOCATE) -DDEBUGCOM3 monitor + $(LOCATE) -d- -DMONITOR -DDEBUGCOM3 $(PRODUCT) software.cfg + +$(PRODUCT_RTB): bcc32.cfg hardware.cfg software.cfg $(PRODUCT_EXE) + @echo Running Locate on $(PRODUCT) + $(LOCATE) $(PRODUCT) software.cfg + +# Linker specific: the link below is for BCC linker/compiler. If you link +# with a different linker - please change accordingly. +# + +# need a temp response file (@&&) because command line is too long +$(PRODUCT_EXE) : $(OBJS) + @echo Running Linker for $(PRODUCT_EXE) + $(LINK) -L$(LINKER_LIB) -m -c -s -v @&&| # temp response file, starts with | + $(BORLAND_DIR)\lib\c0x32.obj $** # $** lists each dependency + $< + $*.map + $(LIBS) +| # end of temp response file + +# +# Utilities + +clean : + @echo Deleting obj files, $(PRODUCT_EXE), $(PRODUCT_RTB) and map files. + del *.obj + del $(PRODUCT_EXE) + del $(PRODUCT_RTB) + del *.map + del bcc32.cfg + +install : $(PRODUCT) + copy $(PRODUCT) ..\bin + +# +# Generic rules +# +.SUFFIXES: .cpp .c .sbr .obj + +# +# cc generic rule +# +.c.obj: + @echo Compiling $@ from $< + $(CC) $(CFLAGS) -c -o$@ $< + +# Compiler configuration file +bcc32.cfg : + Copy &&| +-y #include line numbers in OBJ's +-v #include debug info +-w+ #turn on all warnings +-Od #disable all optimizations +#-a4 #32 bit data alignment +#-M # generate link map +#-ls # linker options +#-WM- #not multithread +-WM #multithread +-w-aus # ignore warning assigned a value that is never used +-w-sig # ignore warning conversion may lose sig digits +| $@ + +# EOF: makefile diff --git a/bacnet-stack/ports/rtos32/monitor.cfg b/bacnet-stack/ports/rtos32/monitor.cfg new file mode 100644 index 00000000..283553ae --- /dev/null +++ b/bacnet-stack/ports/rtos32/monitor.cfg @@ -0,0 +1,46 @@ +// Configuration files for the RTTarget-32 Debug Monitor and Borland C/C++. + +// Some general parameters for this file are: +// +// * The default disk boot code is used to boot the system from +// a floppy disk, hard disk, or ROM disk. +// * Pageing is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The target PC is assumed to have a color display. +// * The target PC uses COM1 to communicated with the host. +// * 115200 baud is used for host - target communication. + + +@HARDWARE.CFG // pull in hardware definitions + +Locate BootCode BIOSBOOT.EXE LowMem // boot from disk +Locate BootData BootData LowMem 0 16 // boot stuff must be in conventional memory +Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code +CPL = 0 + +Locate Section CODE LowMem 1 // Monitor's code section +Locate Header Monitor LowMem 0 4 // and header +Locate Section DATA LowMem 2 // data section +Locate Stack Stack LowMem 1k 4 // and a small stack, no heap +Locate PageTable Pages LowMem + +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy CODE LowMem // compress everything +Locate Copy DATA LowMem // ditto +Locate Copy Pages LowMem // ditto + +#ifdef DEBUGCOM1 +COMPort COM1 115200 // use COM1 with 115200 baud +VideoRAM = None // program output sent to debugger - clrscr() crashes it. +#elifdef DEBUGCOM3 +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +VideoRAM = ColorText // program output sent to debugger - clrscr() crashes it. +#else +COMPort COM3 115200 9 // use COM3 IRQ9 115200 baud - Everex EV170 serial card +VideoRAM = ColorText // program output sent to Graphic Card +#endif + +IgnoreMsg "No heap" // the monitor does not need a heap diff --git a/bacnet-stack/ports/rtos32/software.cfg b/bacnet-stack/ports/rtos32/software.cfg new file mode 100644 index 00000000..3adec06b --- /dev/null +++ b/bacnet-stack/ports/rtos32/software.cfg @@ -0,0 +1,80 @@ +// Configuration files for the application and Borland C/C++. + +// Some general parameters for this file are: + +// * The program will run under the control of the debugger or is +// downloaded using RTRun. +// * Paging is enabled. +// * The program privilege level is set to 3 for maximum protection. +// * Boot code and the Monitor are placed in low (conventional) memory. +// * The program is placed in high (extended) memory. +// * Unused low memory is remapped and appended to the high memory area. +// * The Turbo Debugger symbol tables are pulled in to support +// task positions at source level. + +@HARDWARE.CFG + +#ifdef DEBUGDOS + #define BOOT_DOS +#endif + +#ifdef TSYS_DOS + #define BOOT_DOS +#endif + +#ifsection .text // redefine some section names for BCB + #define CODE .text + #define DATA .data +#endif + +// Either use the monitor, or create bootable code. +#ifdef MONITOR + Reserve Monitor // leave room for Debug Monitor +#elifdef BOOT_DOS + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk, bios, or DOS + Locate BootData BootData LowMem // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges + VideoRAM ColorText // program output sent to Graphics Card +#else + Locate BootCode BIOSBOOT.EXE LowMem // boot from disk + Locate BootData BootData LowMem 0 16 // must be in conventional mem + Locate DiskBuffer DiskIO LowMem 16k 16k // needed by disk boot code + NoFPU=0 // Check FPU + CPL = 3 // normal priveleges + VideoRAM ColorText // program output sent to Graphic Card +#endif + +FillRAM HeapMem // remap unused RAM + +Locate Header Application LowMem // application header +Locate PageTable Paging LowMem 20k // paging to use this + +Locate NTSection CODE ProgMem->HighMem // code section +Locate NTSection DATA ProgMem->HighMem // data section + +#ifsection .tls // the following sections are not generated by all linker versions + Locate NTSection .tls ProgMem->HighMem // TLS data section + Locate NTSection .rdata ProgMem->HighMem // TLS directory +#endif + +Locate Stack Stack StackMem->LowMem 16k // stack space for main() +Locate Heap Heap HeapMem // and the rest for the heap + +// Compression needed if we are short on disk space - but shortens download +// Note that this is discardable, unless we use -d- option of RTLoc +Locate DecompCode Expand LowMem // include decompression stuff +Locate DecompData ExBuffer LowMem + +Locate Copy Paging LowMem // compress Paging +Locate Copy CODE HighMem // compress CODE +Locate Copy DATA HighMem // compress DATA + +#ifndef TSYS_CARD +Locate Nothing FloppyDMA MoreLowMem 18k 64k ReadWrite // floppy driver +#endif + +Init _Init // do some standard initializations (see init.c) + +CommandLine "bacnet.exe" \ No newline at end of file