From 3a84572662673ed01dc71e4bb6a1b6651b33462c Mon Sep 17 00:00:00 2001 From: skarg Date: Wed, 6 Mar 2013 17:32:20 +0000 Subject: [PATCH] Created new port for BSD/MAC OS X (ports/bsd). It supports only BACnet/IP. Thank you, Patrick! --- bacnet-stack/ports/bsd/bip-init.c | 266 +++++++++++++++++++++++++++ bacnet-stack/ports/bsd/main.c | 290 ++++++++++++++++++++++++++++++ bacnet-stack/ports/bsd/net.h | 99 ++++++++++ bacnet-stack/ports/bsd/readme.txt | 2 + bacnet-stack/ports/bsd/stdbool.h | 29 +++ bacnet-stack/ports/bsd/timer.c | 156 ++++++++++++++++ bacnet-stack/ports/bsd/timer.h | 65 +++++++ 7 files changed, 907 insertions(+) create mode 100644 bacnet-stack/ports/bsd/bip-init.c create mode 100644 bacnet-stack/ports/bsd/main.c create mode 100644 bacnet-stack/ports/bsd/net.h create mode 100644 bacnet-stack/ports/bsd/readme.txt create mode 100644 bacnet-stack/ports/bsd/stdbool.h create mode 100644 bacnet-stack/ports/bsd/timer.c create mode 100644 bacnet-stack/ports/bsd/timer.h diff --git a/bacnet-stack/ports/bsd/bip-init.c b/bacnet-stack/ports/bsd/bip-init.c new file mode 100644 index 00000000..f3dac00a --- /dev/null +++ b/bacnet-stack/ports/bsd/bip-init.c @@ -0,0 +1,266 @@ +/*####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####*/ + +#include /* for standard integer types uint8_t etc. */ +#include /* for the standard bool type. */ +#include "bacdcode.h" +#include "bip.h" +#include "net.h" +#include + +/** @file linux/bip-init.c Initializes BACnet/IP interface (BSD/MAC OS X). */ + +bool BIP_Debug = true; + +void * +get_addr_ptr (struct sockaddr * sockaddr_ptr) +{ + void * addr_ptr; + if (sockaddr_ptr->sa_family == AF_INET) { + addr_ptr = &((struct sockaddr_in *) sockaddr_ptr)->sin_addr; + } + else if (sockaddr_ptr->sa_family == AF_INET6) { + addr_ptr = &((struct sockaddr_in6 *) sockaddr_ptr)->sin6_addr; + } + return addr_ptr; +} + +/* gets an IP address by name, where name can be a + string that is an IP address in dotted form, or + a name that is a domain name + returns 0 if not found, or + an IP address in network byte order */ +long bip_getaddrbyname( + const char *host_name) +{ + struct hostent *host_ent; + + if ((host_ent = gethostbyname(host_name)) == NULL) + return 0; + + return *(long *) host_ent->h_addr; +} + +/** Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * + * @param ifname [in] The named interface to use for the network layer. + * Eg, for MAC OS X, ifname is en0, en1, and others. + * @param addr [out] The netmask addr, broadcast addr, ip addr. + * @param request [in] addr broadaddr netmask + */ +static int get_local_address( + char *ifname,struct in_addr *addr,char *request) +{ + + char rv; /* return value */ + + struct ifaddrs * ifaddrs_ptr; + int status; + status = getifaddrs (& ifaddrs_ptr); + if (status == -1) { + fprintf (stderr, "Error in 'getifaddrs': %d (%s)\n", + errno, strerror (errno)); + } + while (ifaddrs_ptr) { + if ((ifaddrs_ptr->ifa_addr->sa_family == AF_INET) && + (strcmp(ifaddrs_ptr->ifa_name, ifname) == 0)) { + void * addr_ptr; + if (! ifaddrs_ptr->ifa_addr) { + return rv; + } + if (strcmp(request, "addr") == 0) { + addr_ptr = get_addr_ptr (ifaddrs_ptr->ifa_addr); + } + else if (strcmp(request, "broadaddr") == 0) { + addr_ptr = get_addr_ptr (ifaddrs_ptr->ifa_broadaddr); + } + else if (strcmp(request, "netmask") == 0) { + addr_ptr = get_addr_ptr (ifaddrs_ptr->ifa_netmask); + } + if (addr_ptr) { + memcpy(addr, addr_ptr, sizeof(struct in_addr)); + } + } + ifaddrs_ptr = ifaddrs_ptr->ifa_next; + } + freeifaddrs (ifaddrs_ptr); + return rv; +} + +/** Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * + * @param ifname [in] The named interface to use for the network layer. + * Eg, for MAC OS X, ifname is en0, en1, and others. + */ +void bip_set_interface( + char *ifname) +{ + struct in_addr local_address; + struct in_addr broadcast_address; + int rv = 0; + + /* setup local address */ + char *request = "addr"; + rv = get_local_address(ifname, &local_address, request); + if (rv < 0) { + local_address.s_addr = 0; + } + bip_set_addr(local_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "Interface: %s\n", ifname); + fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address)); + } + /* setup local broadcast address */ + request = "broadaddr"; + rv = get_local_address(ifname, &broadcast_address, request); + if (rv < 0) { + broadcast_address.s_addr = ~0; + } + bip_set_broadcast_addr(broadcast_address.s_addr); + if (BIP_Debug) { + fprintf(stderr, "IP Broadcast Address: %s\n", + inet_ntoa(broadcast_address)); + fprintf(stderr, "UDP Port: 0x%04X [%hu]\n", ntohs(bip_get_port()), + ntohs(bip_get_port())); + } +} + +/** Initialize the BACnet/IP services at the given interface. + * @ingroup DLBIP + * -# Gets the local IP address and local broadcast address from the system, + * and saves it into the BACnet/IP data structures. + * -# Opens a UDP socket + * -# Configures the socket for sending and receiving + * -# Configures the socket so it can send broadcasts + * -# Binds the socket to the local IP address at the specified port for + * BACnet/IP (by default, 0xBAC0 = 47808). + * + * @note For MAC OS X, ifname is en0, en1, and others. + * + * @param ifname [in] The named interface to use for the network layer. + * If NULL, the "en0" interface is assigned. + * @return True if the socket is successfully opened for BACnet/IP, + * else False if the socket functions fail. + */ +bool bip_init( + char *ifname) +{ + int status = 0; /* return from socket lib calls */ + struct sockaddr_in sin; + int sockopt = 0; + int sock_fd = -1; + + if (ifname) { + bip_set_interface(ifname); + printf("interface %s", ifname); + } else { + bip_set_interface("en0"); + } + /* assumes that the driver has already been initialized */ + sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + bip_set_socket(sock_fd); + if (sock_fd < 0) + return false; + /* Allow us to use the same socket for sending and receiving */ + /* This makes sure that the src port is correct when sending */ + sockopt = 1; + status = + setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return status; + } + /* allow us to send a broadcast */ + status = + setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST, &sockopt, + sizeof(sockopt)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + 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_get_port(); + memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero)); + status = + bind(sock_fd, (const struct sockaddr *) &sin, sizeof(struct sockaddr)); + if (status < 0) { + close(sock_fd); + bip_set_socket(-1); + return false; + } + + return true; +} + +/** Cleanup and close out the BACnet/IP services by closing the socket. + * @ingroup DLBIP + */ +void bip_cleanup( + void) +{ + int sock_fd = 0; + + if (bip_valid()) { + sock_fd = bip_socket(); + close(sock_fd); + } + bip_set_socket(-1); + + return; +} + +/** Get the netmask of the BACnet/IP's interface via an getifaddrs() call. + * @param netmask [out] The netmask, in host order. + * @return 0 on success, else the error from the getifaddrs() call. + */ +int bip_get_local_netmask( + struct in_addr *netmask) +{ + int rv; + char *ifname = getenv("BACNET_IFACE"); /* will probably be null */ + if (ifname == NULL) + ifname = "en0"; + printf("ifname %s",ifname); + char *request = "netmask"; + rv = get_local_address(ifname, netmask, request); + + return rv; +} diff --git a/bacnet-stack/ports/bsd/main.c b/bacnet-stack/ports/bsd/main.c new file mode 100644 index 00000000..a72d192b --- /dev/null +++ b/bacnet-stack/ports/bsd/main.c @@ -0,0 +1,290 @@ +/************************************************************************** +* +* 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 +#include +#include +#include +#include +#include "config.h" +#include "address.h" +#include "bacdef.h" +#include "handlers.h" +#include "client.h" +#include "bacdcode.h" +#include "npdu.h" +#include "apdu.h" +#include "iam.h" +#include "tsm.h" +#include "device.h" +#include "bacfile.h" +#include "datalink.h" +#include "net.h" +#include "txbuf.h" +#include "dlenv.h" + +/** @file bsd/main.c Example application using the BACnet Stack on BSD/MAC OS X. */ + +bool Who_Is_Request = true; + +/* buffers used for receiving */ +static uint8_t Rx_Buf[MAX_MPDU] = { 0 }; + +static void LocalIAmHandler( + uint8_t * service_request, + uint16_t service_len, + BACNET_ADDRESS * src) +{ + int len = 0; + uint32_t device_id = 0; + unsigned max_apdu = 0; + int segmentation = 0; + uint16_t vendor_id = 0; + + (void) src; + (void) service_len; + len = + iam_decode_service_request(service_request, &device_id, &max_apdu, + &segmentation, &vendor_id); + fprintf(stderr, "Received I-Am Request"); + if (len != -1) { + fprintf(stderr, " from %u!\n", device_id); + address_add(device_id, max_apdu, src); + } else + fprintf(stderr, "!\n"); + + return; +} + +static void Read_Properties( + void) +{ + uint32_t device_id = 0; + bool status = false; + unsigned max_apdu = 0; + BACNET_ADDRESS src; + bool next_device = false; + static unsigned index = 0; + static unsigned property = 0; + /* list of required (and some optional and proprietary) + properties in the Device Object. Note that this demo + tests for error messages so that the device doesn't have + to have all the properties listed here. */ + const int object_props[] = { + PROP_OBJECT_IDENTIFIER, + PROP_OBJECT_NAME, + PROP_OBJECT_TYPE, + PROP_SYSTEM_STATUS, + PROP_VENDOR_NAME, + PROP_VENDOR_IDENTIFIER, + PROP_MODEL_NAME, + PROP_FIRMWARE_REVISION, + PROP_APPLICATION_SOFTWARE_VERSION, + PROP_PROTOCOL_VERSION, + PROP_PROTOCOL_SERVICES_SUPPORTED, + PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED, + PROP_MAX_APDU_LENGTH_ACCEPTED, + PROP_SEGMENTATION_SUPPORTED, + PROP_LOCAL_TIME, + PROP_LOCAL_DATE, + PROP_UTC_OFFSET, + PROP_DAYLIGHT_SAVINGS_STATUS, + PROP_APDU_SEGMENT_TIMEOUT, + PROP_APDU_TIMEOUT, + PROP_NUMBER_OF_APDU_RETRIES, + PROP_TIME_SYNCHRONIZATION_RECIPIENTS, + PROP_MAX_MASTER, + PROP_MAX_INFO_FRAMES, + PROP_DEVICE_ADDRESS_BINDING, + /* note: PROP_OBJECT_LIST is missing because + the result can be very large. Read index 0 + which gives us the number of objects in the list, + and then we can read index 1, 2.. n one by one, + rather than trying to read the entire object + list in one message. */ + /* some proprietary properties */ + 514, 515, + /* end of list */ + -1 + }; + + if (address_count()) { + if (address_get_by_index(index, &device_id, &max_apdu, &src)) { + if (object_props[property] < 0) + next_device = true; + else { + /* note: if we wanted to do this synchronously, we would get the + invoke ID from the sending of the request, and wait until we + got the reply with matching invoke ID or the TSM of the + invoke ID expired. This demo is doing things asynchronously. */ + status = Send_Read_Property_Request(device_id, /* destination device */ + OBJECT_DEVICE, device_id, object_props[property], + BACNET_ARRAY_ALL); + if (status) + property++; + } + } else + next_device = true; + if (next_device) { + next_device = false; + index++; + if (index >= MAX_ADDRESS_CACHE) + index = 0; + property = 0; + } + } + + return; +} + +static void Init_Service_Handlers( + void) +{ + Device_Init(NULL); + handler_read_property_object_set(OBJECT_DEVICE, + Device_Encode_Property_APDU, Device_Valid_Object_Instance_Number); + /* we need to handle who-is to support dynamic device binding */ + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS, handler_who_is); + apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM, LocalIAmHandler); + + /* 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 + (handler_unrecognized_service); + /* Set the handlers for any confirmed services that we support. */ + /* We must implement read property - it's required! */ + apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property); + /* handle the data coming back from confirmed requests */ + apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY, + handler_read_property_ack); +} + +static void print_address_cache( + void) +{ + unsigned i, j; + BACNET_ADDRESS address; + uint32_t device_id = 0; + unsigned max_apdu = 0; + + fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n"); + for (i = 0; i < MAX_ADDRESS_CACHE; i++) { + if (address_get_by_index(i, &device_id, &max_apdu, &address)) { + fprintf(stderr, "%u\t", device_id); + for (j = 0; j < address.mac_len; j++) { + fprintf(stderr, "%02X", address.mac[j]); + } + fprintf(stderr, "\t"); + fprintf(stderr, "%hu\t", max_apdu); + fprintf(stderr, "%hu\n", address.net); + } + } +} + +static void print_tsm_stats( + void) +{ + int idle = 0; + int total = 0; + + idle = tsm_transaction_idle_count(); + total = MAX_TSM_TRANSACTIONS; + fprintf(stderr, "TSM: %d idle of %d transactions\n", idle, total); +} + +static void sig_handler( + int signo) +{ + datalink_cleanup(); + print_address_cache(); + print_tsm_stats(); + + exit(0); +} + +int main( + int argc, + char *argv[]) +{ + BACNET_ADDRESS src = { 0 }; /* address where message came from */ + uint16_t pdu_len = 0; + unsigned timeout = 100; /* milliseconds */ + unsigned count = 0; /* milliseconds */ + time_t start_time; + time_t new_time = 0; + + start_time = time(NULL); /* get current time */ + /* Linux specials */ + signal(SIGINT, sig_handler); + signal(SIGHUP, sig_handler); + signal(SIGTERM, sig_handler); + /* setup this BACnet Server device */ + Device_Set_Object_Instance_Number(111); + Init_Service_Handlers(); + dlenv_init(); + /* loop forever */ + for (;;) { + /* input */ + new_time = time(NULL); + /* returns 0 bytes on timeout */ + pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout); + + /* process */ + if (pdu_len) { + npdu_handler(&src, &Rx_Buf[0], pdu_len); + } + if (new_time > start_time) { + tsm_timer_milliseconds(new_time - start_time * 1000); + start_time = new_time; + } + if (I_Am_Request) { + I_Am_Request = false; + Send_I_Am(&Handler_Transmit_Buffer[0]); + } else if (Who_Is_Request) { + Who_Is_Request = false; + Send_WhoIs(-1, -1); + } + /* output */ + /* some round robin task switching */ + count++; + switch (count) { + case 1: + /* used for testing, but kind of noisy on the network */ + /*Read_Properties(); */ + break; + case 2: + break; + default: + count = 0; + break; + } + + /* blink LEDs, Turn on or off outputs, etc */ + } + + return 0; +} diff --git a/bacnet-stack/ports/bsd/net.h b/bacnet-stack/ports/bsd/net.h new file mode 100644 index 00000000..3c73d7f3 --- /dev/null +++ b/bacnet-stack/ports/bsd/net.h @@ -0,0 +1,99 @@ +/************************************************************************** +* +* 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. +* +*********************************************************************/ + +#ifndef NET_H +#define NET_H + +/* common unix sockets headers needed */ +#include /* basic system data types */ +#include /* timeval{} for select() */ +#include /* timespec{} for pselect() */ +#include /* sockaddr_in{} and other Internet defns */ +#include /* inet(3) functions */ +#include /* for nonblocking */ +#include +#include +#include +#include +#include +#include +#include /* for S_xxx file mode constants */ +#include /* for iovec{} and readv/writev */ +#include +#include +#include /* for Unix domain sockets */ + +#ifdef HAVE_SYS_SELECT_H +# include /* for convenience */ +#endif + +#ifdef HAVE_POLL_H +# include /* for convenience */ +#endif + +#ifdef HAVE_STRINGS_H +# include /* for convenience */ +#endif + +/* Three headers are normally needed for socket/file ioctl's: + * , , and . + */ +#ifdef HAVE_SYS_IOCTL_H +# include +#endif +#ifdef HAVE_SYS_FILIO_H +# include +#endif +#ifdef HAVE_SYS_SOCKIO_H +# include +#endif + +#include +#include + +#define ENUMS +#include +#include +#include +#include +#include +#include +#include +#include /* the L2 protocols */ +#include +#include +#include +#include +#include +#include + +/** @file bsd/net.h Includes BSD network headers. */ + +/* Local helper functions for this port */ +extern int bip_get_local_netmask( + struct in_addr *netmask); + + +#endif diff --git a/bacnet-stack/ports/bsd/readme.txt b/bacnet-stack/ports/bsd/readme.txt new file mode 100644 index 00000000..fb7e766f --- /dev/null +++ b/bacnet-stack/ports/bsd/readme.txt @@ -0,0 +1,2 @@ +This is a port to MAC OS X for testing. +The unit tests can be run via the test.sh script. diff --git a/bacnet-stack/ports/bsd/stdbool.h b/bacnet-stack/ports/bsd/stdbool.h new file mode 100644 index 00000000..71a283e0 --- /dev/null +++ b/bacnet-stack/ports/bsd/stdbool.h @@ -0,0 +1,29 @@ +#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/bsd/timer.c b/bacnet-stack/ports/bsd/timer.c new file mode 100644 index 00000000..bca8bdbb --- /dev/null +++ b/bacnet-stack/ports/bsd/timer.c @@ -0,0 +1,156 @@ +/************************************************************************** +* +* Copyright (C) 2009 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 +#include +#include "timer.h" +#include +#include + +/** @file bsd/timer.c Provides BSD-specific time and timer functions. */ + +/* counter for the various timers */ +static volatile uint32_t Millisecond_Counter[MAX_MILLISECOND_TIMERS]; + +/* start time for the clock */ +static struct timespec start; +/* The timeGetTime function retrieves the system time, in milliseconds. + The system time is the time elapsed since Windows was started. */ +uint32_t timeGetTime( + void) +{ + struct timespec now; + uint32_t ticks; + + // clock_gettime(CLOCK_MONOTONIC, &now); + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + now.tv_sec = mts.tv_sec; + now.tv_nsec = mts.tv_nsec; + + ticks = + (now.tv_sec - start.tv_sec) * 1000 + (now.tv_nsec - + start.tv_nsec) / 1000000; + + return ticks; +} + +/************************************************************************* +* Description: returns the current millisecond count +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_milliseconds( + unsigned index) +{ + uint32_t now = timeGetTime(); + uint32_t delta_time = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + if (Millisecond_Counter[index] <= now) { + delta_time = now - Millisecond_Counter[index]; + } else { + delta_time = (UINT32_MAX - Millisecond_Counter[index]) + now + 1; + } + } + + return delta_time; +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value) +{ + return (timer_milliseconds(index) >= value); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_seconds( + unsigned index, + uint32_t seconds) +{ + return ((timer_milliseconds(index) / 1000) >= seconds); +} + +/************************************************************************* +* Description: compares the current time count with a value +* Returns: true if the time has elapsed +* Notes: none +*************************************************************************/ +bool timer_elapsed_minutes( + unsigned index, + uint32_t minutes) +{ + return ((timer_milliseconds(index) / (1000 * 60)) >= minutes); +} + +/************************************************************************* +* Description: Sets the timer counter to zero. +* Returns: none +* Notes: none +*************************************************************************/ +uint32_t timer_reset( + unsigned index) +{ + uint32_t timer_value = 0; + + if (index < MAX_MILLISECOND_TIMERS) { + timer_value = timer_milliseconds(index); + Millisecond_Counter[index] = timeGetTime(); + } + + return timer_value; +} + +/************************************************************************* +* Description: Initialization for timer +* Returns: none +* Notes: none +*************************************************************************/ +void timer_init( + void) +{ + //clock_gettime(CLOCK_MONOTONIC, &start); + clock_serv_t cclock; + mach_timespec_t mts; + host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &cclock); + clock_get_time(cclock, &mts); + mach_port_deallocate(mach_task_self(), cclock); + start.tv_sec = mts.tv_sec; + start.tv_nsec = mts.tv_nsec; +} diff --git a/bacnet-stack/ports/bsd/timer.h b/bacnet-stack/ports/bsd/timer.h new file mode 100644 index 00000000..a2fff783 --- /dev/null +++ b/bacnet-stack/ports/bsd/timer.h @@ -0,0 +1,65 @@ +/************************************************************************** +* +* Copyright (C) 2009 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 +#include +#include /* for timeval */ + +/* Timer Module */ +#ifndef MAX_MILLISECOND_TIMERS +#define TIMER_SILENCE 0 +#define MAX_MILLISECOND_TIMERS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + uint32_t timeGetTime( + void); + + void timer_init( + void); + uint32_t timer_milliseconds( + unsigned index); + bool timer_elapsed_milliseconds( + unsigned index, + uint32_t value); + bool timer_elapsed_seconds( + unsigned index, + uint32_t value); + bool timer_elapsed_minutes( + unsigned index, + uint32_t seconds); + uint32_t timer_milliseconds_set( + unsigned index, + uint32_t value); + uint32_t timer_reset( + unsigned index); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif