diff --git a/bacnet-stack/demo/Makefile b/bacnet-stack/demo/Makefile
index 502a5b89..83b82dd0 100644
--- a/bacnet-stack/demo/Makefile
+++ b/bacnet-stack/demo/Makefile
@@ -49,6 +49,10 @@ SUBDIRS = readprop writeprop readfile writefile reinit server dcc \
whohas whois ucov scov timesync epics readpropm \
mstpcap mstpcrc uptransfer \
whoisrouter iamrouter initrouter
+ifeq (${BACNET_PORT},linux)
+SUBDIRS += router
+endif
+
ifeq (${BACNET_PORT},win32)
SUBDIRS += ptransfer
endif
diff --git a/bacnet-stack/demo/handler/h_get_alarm_sum.c b/bacnet-stack/demo/handler/h_get_alarm_sum.c
index 5d670e5b..db3fe68f 100644
--- a/bacnet-stack/demo/handler/h_get_alarm_sum.c
+++ b/bacnet-stack/demo/handler/h_get_alarm_sum.c
@@ -152,7 +152,7 @@ void handler_get_alarm_summary(
pdu_len);
#if PRINT_ENABLED
if (bytes_sent <= 0) {
- fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
+ //fprintf(stderr, "Failed to send PDU (%s)!\n", strerror(errno));
}
#else
bytes_sent = bytes_sent;
diff --git a/bacnet-stack/demo/router/Makefile b/bacnet-stack/demo/router/Makefile
new file mode 100644
index 00000000..94cb86df
--- /dev/null
+++ b/bacnet-stack/demo/router/Makefile
@@ -0,0 +1,63 @@
+#Makefile to build BACnet Application for the Linux Port
+
+# tools - only if you need them.
+# Most platforms have this already defined
+# CC = gcc
+
+# Executable file name
+TARGET = router
+
+TARGET_BIN = ${TARGET}$(TARGET_EXT)
+
+ifeq (${BACNET_PORT},linux)
+#PFLAGS =
+# -pthread
+TARGET_EXT =
+LIBS = -lpthread -lconfig
+LFLAGS += $(LIBS)
+endif
+
+#DEFINES = $(BACNET_DEFINES) -DBACDL_MSTP -DBACDL_BIP
+BACNET_SOURCE_DIR = ../../src
+
+SRCS = main.c \
+ ${BACNET_PORT_DIR}/rs485.c \
+ ${BACNET_PORT_DIR}/timer.c \
+ ${BACNET_PORT_DIR}/bip-init.c \
+ ${BACNET_PORT_DIR}/dlmstp_linux.c \
+ ${BACNET_SOURCE_DIR}/bip.c \
+ ${BACNET_SOURCE_DIR}/bvlc.c \
+ ${BACNET_SOURCE_DIR}/fifo.c \
+ ${BACNET_SOURCE_DIR}/mstp.c \
+ ${BACNET_SOURCE_DIR}/mstptext.c \
+ ${BACNET_SOURCE_DIR}/debug.c \
+ ${BACNET_SOURCE_DIR}/indtext.c \
+ ${BACNET_SOURCE_DIR}/ringbuf.c \
+ ${BACNET_SOURCE_DIR}/crc.c \
+ mstpmodule.c \
+ ipmodule.c \
+ portthread.c \
+ msgqueue.c \
+ network_layer.c
+
+
+OBJS = ${SRCS:.c=.o}
+
+all: Makefile ${TARGET_BIN}
+
+${TARGET_BIN}: ${OBJS} Makefile
+ ${CC} ${PFLAGS} ${OBJS} ${LFLAGS} -o $@
+ size $@
+ cp $@ ../../bin
+
+.c.o:
+ ${CC} -c ${CFLAGS} $*.c -o $@
+
+depend:
+ rm -f .depend
+ ${CC} -MM ${CFLAGS} *.c >> .depend
+
+clean:
+ rm -f core ${TARGET_BIN} ${OBJS} $(TARGET).map
+
+include: .depend
diff --git a/bacnet-stack/demo/router/init.cfg b/bacnet-stack/demo/router/init.cfg
new file mode 100644
index 00000000..ed0d9aba
--- /dev/null
+++ b/bacnet-stack/demo/router/init.cfg
@@ -0,0 +1,68 @@
+/*
+configuration file that stores values for router ports initialization
+
+Common arguments:
+ device_type - "bip" or "mstp" (with quotes)
+ device - Connection device, for example "eth0" or "/dev/ttyS0"
+ network - Network number [1..65534]. Do not use network number 65535, it is broadcast number
+
+bip arguments:
+ port - bip UDP port, default 47808
+
+mstp arguments:
+ mac - MSTP MAC
+ max_master - MSTP max master
+ max_frames - 1
+ baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400
+ parity - one from the list (with quotes): "None", "Even", "Odd"
+ databits - one from the list: 5, 6, 7, 8
+ stopbits - 1 or 2
+
+
+Example:
+ ports =
+ (
+ {
+ device_type = "bip";
+ device = "eth0";
+ port = 47808;
+ network = 1;
+ },
+
+ {
+ device_type = "mstp";
+ device = "/dev/ttyS0";
+ mac = 1;
+ max_master = 127;
+ max_frames = 1;
+ baud = 38400;
+ parity = "None";
+ databits = 8;
+ stopbits = 1;
+ network = 2;
+ }
+ );
+*/
+
+ports =
+(
+ {
+ device_type = "bip";
+ device = "eth0";
+ port = 47808;
+ network = 1;
+ },
+
+ {
+ device_type = "mstp";
+ device = "/dev/ttyS0";
+ mac = 2;
+ max_master = 127;
+ max_frames = 1;
+ baud = 38400;
+ parity = "None";
+ databits = 8;
+ stopbits = 1;
+ network = 2;
+ }
+);
diff --git a/bacnet-stack/demo/router/ipmodule.c b/bacnet-stack/demo/router/ipmodule.c
new file mode 100644
index 00000000..bc83654c
--- /dev/null
+++ b/bacnet-stack/demo/router/ipmodule.c
@@ -0,0 +1,375 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include "ipmodule.h"
+#include "bacint.h"
+
+#ifdef TEST_PACKET
+ uint8_t test_packet[]={0x81, 0x0a, 0x00, 0x16, // BVLC header
+ 0x01, 0x24, 0x00, 0x01, 0x01, 0x0b, 0xff, // NPDU
+ 0x00, 0x03, 0x01, 0x0c, 0x0c, 0x00, 0x00, 0x00, 0x02, 0x19, 0x55}; // APDU
+#endif
+
+extern int get_local_address_ioctl(
+ char *ifname,
+ struct in_addr *addr,
+ int request);
+
+void* dl_ip_thread(void *pArgs) {
+ MSGBOX_ID msgboxid;
+ BACMSG msg_storage, *bacmsg = NULL;
+ MSG_DATA *msg_data;
+ ROUTER_PORT *port = (ROUTER_PORT*)pArgs;
+ IP_DATA ip_data; // port specific parameters
+ BACNET_ADDRESS address = { 0 };
+ int status;
+ uint8_t shutdown = 0;
+
+ // initialize router port
+ if (!dl_ip_init(port, &ip_data)) {
+ port->state = INIT_FAILED;
+ return NULL;
+ }
+
+ // allocate buffer
+ ip_data.max_buff = MAX_BIP_MPDU;
+ ip_data.buff = (uint8_t*)malloc(ip_data.max_buff);
+
+ if (ip_data.buff == NULL) {
+ port->state = INIT_FAILED;
+ return NULL;
+ }
+
+ msgboxid = create_msgbox();
+ if (msgboxid == INVALID_MSGBOX_ID) {
+ PRINT(ERROR, "Error: Failed to create message box");
+ port->state = INIT_FAILED;
+ return NULL;
+ }
+
+ port->port_id = msgboxid;
+ port->state = RUNNING;
+
+ while (!shutdown) {
+
+ // check for incoming messages
+ bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
+
+ if (bacmsg) {
+ switch(bacmsg->type) {
+ case DATA: {
+ msg_data = (MSG_DATA*)bacmsg->data;
+ memmove(&address.net, &msg_data->dest.net, 2);
+ memmove(&address.mac_len, &msg_data->dest.len, 1);
+ memmove(&address.mac[0], &msg_data->dest.adr[0], MAX_MAC_LEN);
+
+ dl_ip_send(&ip_data, &address, msg_data->pdu, msg_data->pdu_len);
+
+ check_data(msg_data);
+
+ break;
+ }
+
+ case SERVICE: {
+ switch(bacmsg->subtype) {
+ case SHUTDOWN:
+ del_msgbox(port->port_id);
+ shutdown = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+ } else {
+ status = dl_ip_recv(&ip_data, &msg_data, &address, 1000);
+ if (status > 0) {
+ memmove(&msg_data->src.len, &address.mac_len, 1);
+ memmove(&msg_data->src.adr[0], &address.mac[0], MAX_MAC_LEN);
+ msg_storage.origin = port->port_id;
+ msg_storage.type = DATA;
+ msg_storage.data = msg_data;
+
+ if (!send_to_msgbox(port->main_id, &msg_storage)) {
+ free_data(msg_data);
+ }
+ }
+ }
+ }
+
+ // cleanup procedure
+ dl_ip_cleanup(&ip_data);
+ port->state = FINISHED;
+ return NULL;
+}
+
+bool dl_ip_init(ROUTER_PORT *port,
+ IP_DATA *ip_data) {
+ struct sockaddr_in sin;
+ int socket_opt = 0;
+ int status = 0; // for error checking
+
+ // setup port for later use
+ ip_data->port = htons(port->params.bip_params.port);
+
+ // get local address
+ status = get_local_address_ioctl(port->iface, &ip_data->local_addr, SIOCGIFADDR);
+ if (status < 0) {
+ return false;
+ }
+ // get broadcast address
+ status = get_local_address_ioctl(port->iface, &ip_data->broadcast_addr, SIOCGIFBRDADDR);
+ if (status < 0) {
+ return false;
+ }
+
+ ip_data->socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (ip_data->socket < 0)
+ return false;
+
+ // setup socket options
+
+ socket_opt = 1;
+ status = setsockopt(ip_data->socket, SOL_SOCKET, SO_REUSEADDR, &socket_opt, sizeof(socket_opt));
+ if (status < 0) {
+ close(ip_data->socket);
+ return false;
+ }
+
+ status = setsockopt(ip_data->socket, SOL_SOCKET, SO_BROADCAST, &socket_opt, sizeof(socket_opt));
+ if (status < 0) {
+ close(ip_data->socket);
+ return false;
+ }
+
+ // bind the socket to the local port number
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ sin.sin_port = ip_data->port;
+
+ memset(&sin.sin_zero, '\0', sizeof(sin.sin_zero));
+
+ status = bind(ip_data->socket, (const struct sockaddr *) &sin, sizeof(struct sockaddr));
+ if (status < 0) {
+ close(ip_data->socket);
+ return false;
+ }
+
+ // add BIP address to router port structure
+ memcpy(&port->route_info.mac[0], &ip_data->local_addr.s_addr, 4);
+ memcpy(&port->route_info.mac[4], &port->params.bip_params.port, 2);
+ port->route_info.mac_len = 6;
+
+ PRINT(INFO, "Interface: %s\n", port->iface);
+ PRINT(INFO, "IP Address: %s\n", inet_ntoa(ip_data->local_addr));
+ PRINT(INFO, "IP Broadcast Address: %s\n", inet_ntoa(ip_data->broadcast_addr));
+ PRINT(INFO, "UDP Port: 0x%04X [%hu]\n", (port->params.bip_params.port),
+ (port->params.bip_params.port));
+
+ return true;
+}
+
+int dl_ip_send(IP_DATA *data,
+ BACNET_ADDRESS *dest,
+ uint8_t *pdu,
+ unsigned pdu_len)
+{
+ struct sockaddr_in bip_dest = { 0 };
+ int buff_len = 0;
+ int bytes_sent = 0;
+
+ if (data->socket < 0)
+ return -1;
+
+ data->buff[0] = BVLL_TYPE_BACNET_IP;
+ bip_dest.sin_family = AF_INET;
+ if (dest->net == BACNET_BROADCAST_NETWORK) {
+ // broadcast
+ bip_dest.sin_addr.s_addr = data->broadcast_addr.s_addr;
+ bip_dest.sin_port = data->port;
+ data->buff[1] = BVLC_ORIGINAL_BROADCAST_NPDU;
+ } else if (dest->mac_len == 6) {
+ memcpy(&bip_dest.sin_addr.s_addr, &dest->mac[0], 4);
+ memcpy(&bip_dest.sin_port, &dest->mac[4], 2);
+ data->buff[1] = BVLC_ORIGINAL_UNICAST_NPDU;
+ } else {
+ // invalid address
+ return -1;
+ }
+
+ buff_len = 2;
+ buff_len +=
+ encode_unsigned16(&data->buff[buff_len],
+ (uint16_t) (pdu_len + 4 /*inclusive*/ ));
+ memcpy(&data->buff[buff_len], pdu, pdu_len);
+ buff_len += pdu_len;
+
+ // send the packet
+ bytes_sent =
+ sendto(data->socket, (char *) data->buff, buff_len, 0,
+ (struct sockaddr *) &bip_dest, sizeof(struct sockaddr));
+
+ PRINT(DEBUG, "send to %s\n", inet_ntoa(bip_dest.sin_addr));
+
+ return bytes_sent;
+}
+
+int dl_ip_recv(
+ IP_DATA *data,
+ MSG_DATA **msg_data,
+ BACNET_ADDRESS *src,
+ unsigned timeout)
+{
+ int received_bytes = 0;
+ uint16_t buff_len = 0; // return value
+ fd_set read_fds;
+ struct timeval select_timeout;
+ struct sockaddr_in sin = { 0 };
+ socklen_t sin_len = sizeof(sin);
+
+ // make sure the socket is open
+ if (data->socket < 0)
+ return 0;
+
+ 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(data->socket, &read_fds);
+
+#ifdef TEST_PACKET
+ received_bytes = sizeof(test_packet);
+ memmove(data->buff, &test_packet, received_bytes);
+ sin.sin_addr.s_addr = 0x7E1D40A; sin.sin_port = 0xC0BA;
+#else
+ int ret = select(data->socket + 1, &read_fds, NULL, NULL, &select_timeout);
+ // see if there is a packet for us
+ if (ret > 0)
+ received_bytes =
+ recvfrom(data->socket, (char *) &data->buff[0], data->max_buff, 0,
+ (struct sockaddr *) &sin, &sin_len);
+ else
+ return 0;
+#endif
+ PRINT(DEBUG, "received from %s\n", inet_ntoa(sin.sin_addr));
+
+ // check for errors
+ if (received_bytes <= 0) {
+ return 0;
+ }
+
+ // the signature of a BACnet/IP packet
+ if (data->buff[0] != BVLL_TYPE_BACNET_IP)
+ return 0;
+
+ switch (data->buff[1]) {
+ case BVLC_ORIGINAL_UNICAST_NPDU:
+ case BVLC_ORIGINAL_BROADCAST_NPDU: {
+ if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
+ (sin.sin_port == data->port)) {
+ buff_len = 0;
+
+ PRINT(DEBUG, "BIP: src is me. Discarded!\n");
+
+ } else {
+ src->mac_len = 6;
+ memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
+ memcpy(&src->mac[4], &sin.sin_port, 2);
+
+ (void) decode_unsigned16(&data->buff[2], &buff_len);
+ // subtract off the BVLC header
+ buff_len -= 4;
+ if (buff_len < data->max_buff) {
+ // allocate data message stucture
+ (*msg_data) = (MSG_DATA*)malloc(sizeof(MSG_DATA));
+ (*msg_data)->pdu_len = buff_len;
+ (*msg_data)->pdu = (uint8_t*)malloc((*msg_data)->pdu_len);
+ // fill up data message structure
+ memmove(&(*msg_data)->pdu[0], &data->buff[4], (*msg_data)->pdu_len);
+ memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
+ }
+ // ignore packets that are too large
+ else {
+ buff_len = 0;
+
+ PRINT(ERROR, "BIP: PDU too large. Discarded!.\n");
+
+ }
+ }
+ }
+ break;
+
+ case BVLC_FORWARDED_NPDU: {
+ memcpy(&sin.sin_addr.s_addr, &data->buff[4], 4);
+ memcpy(&sin.sin_port, &data->buff[8], 2);
+ if ((sin.sin_addr.s_addr == data->local_addr.s_addr) &&
+ (sin.sin_port == data->port)) {
+ buff_len = 0;
+ } else {
+ src->mac_len = 6;
+ memcpy(&src->mac[0], &sin.sin_addr.s_addr, 4);
+ memcpy(&src->mac[4], &sin.sin_port, 2);
+
+ (void) decode_unsigned16(&data->buff[2], &buff_len);
+ // subtract off the BVLC header
+ buff_len -= 10;
+ if (buff_len < data->max_buff) {
+ // allocate data message stucture
+ (*msg_data) = (MSG_DATA*)malloc(sizeof(MSG_DATA));
+ (*msg_data)->pdu_len = buff_len;
+ (*msg_data)->pdu = (uint8_t*)malloc((*msg_data)->pdu_len);
+ // fill up data message structure
+ memmove(&(*msg_data)->pdu, &data->buff[4+6], (*msg_data)->pdu_len);
+ memmove(&(*msg_data)->src, src, sizeof(BACNET_ADDRESS));
+ } else {
+ // ignore packets that are too large
+ buff_len = 0;
+ }
+ }
+ }
+ break;
+ default:
+
+ PRINT(ERROR, "BIP: BVLC discarded!\n");
+
+ break;
+ }
+ return buff_len;
+}
+
+void dl_ip_cleanup(
+ IP_DATA *ip_data) {
+ // free buffer
+ if (ip_data->buff)
+ free(ip_data->buff);
+ // close socket
+ if (ip_data->socket > 0)
+ close(ip_data->socket);
+ return;
+}
+
diff --git a/bacnet-stack/demo/router/ipmodule.h b/bacnet-stack/demo/router/ipmodule.h
new file mode 100644
index 00000000..ab6402fb
--- /dev/null
+++ b/bacnet-stack/demo/router/ipmodule.h
@@ -0,0 +1,61 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#ifndef UDPMODULE_H
+#define UDPMODULE_H
+
+#include
+#include
+#include "portthread.h"
+#include "bip.h"
+
+#define MAX_BIP_APDU 1476
+#define MAX_BIP_PDU (MAX_NPDU + MAX_BIP_APDU)
+#define MAX_BIP_MPDU (MAX_HEADER + MAX_BIP_PDU)
+
+ typedef struct ip_data {
+ int socket;
+ uint16_t port;
+ struct in_addr local_addr;
+ struct in_addr broadcast_addr;
+ uint8_t *buff;
+ uint16_t max_buff;
+ } IP_DATA;
+
+
+void* dl_ip_thread(
+ void *pArgs);
+
+bool dl_ip_init(
+ ROUTER_PORT *port,
+ IP_DATA *data);
+
+int dl_ip_send(
+ IP_DATA *data,
+ BACNET_ADDRESS *dest,
+ uint8_t *pdu,
+ unsigned pdu_len);
+
+int dl_ip_recv(
+ IP_DATA *data,
+ MSG_DATA **msg, // on recieve fill up message
+ BACNET_ADDRESS *src,
+ unsigned timeout);
+
+void dl_ip_cleanup(
+ IP_DATA *data);
+
+#endif /* end of UDPMODULE_H */
diff --git a/bacnet-stack/demo/router/main.c b/bacnet-stack/demo/router/main.c
new file mode 100644
index 00000000..8adc35b4
--- /dev/null
+++ b/bacnet-stack/demo/router/main.c
@@ -0,0 +1,813 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include
+#include
+#include
+#include // for time
+#include
+#include
+#include
+#include // read config files
+#include // for getopt
+#include // used in kbhit()
+#include
+#include
+#include
+#include
+#include
+#include "msgqueue.h"
+#include "portthread.h"
+#include "network_layer.h"
+#include "ipmodule.h"
+#include "mstpmodule.h"
+#include "bip.h"
+#include "dlmstp.h"
+
+#define KEY_ESC 27
+
+ROUTER_PORT *head = NULL; // pointer to list of router ports
+
+int port_count;
+
+void print_help();
+
+bool read_config(
+ char *filepath);
+
+bool parse_cmd(
+ int argc,
+ char *argv[]);
+
+void init_port_threads(
+ ROUTER_PORT* port_list);
+
+bool init_router();
+
+void cleanup();
+
+void print_msg(
+ BACMSG *msg);
+
+uint16_t process_msg(
+ BACMSG *msg,
+ MSG_DATA *data,
+ uint8_t **buff);
+
+uint16_t get_next_free_dnet();
+
+int kbhit();
+
+inline bool is_network_msg(
+ BACMSG *msg);
+
+int main(
+ int argc,
+ char *argv[])
+{
+ printf("I am router\n");
+
+ ROUTER_PORT *port;
+ BACMSG msg_storage, *bacmsg = NULL;
+ MSG_DATA *msg_data = NULL;
+ uint8_t *buff = NULL;
+ int16_t buff_len = 0;
+
+ atexit(cleanup);
+
+ if (!parse_cmd(argc, argv))
+ {
+ printf("parse cmd failed\r\n");
+ return -1;
+ }
+
+ if(!init_router())
+ {
+ printf("init_router failed\r\n");
+ return -1;
+ }
+
+
+ send_network_message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK, msg_data, &buff, NULL);
+
+ while (true) {
+ if(kbhit()) {
+ char ch = getchar();
+ if (ch == KEY_ESC) {
+ PRINT(INFO, "Received shutdown. Exiting...\n");
+ break;
+ }
+ }
+
+ bacmsg = recv_from_msgbox(head->main_id, &msg_storage);
+ if (bacmsg) {
+ switch (bacmsg->type) {
+ case DATA:
+ {
+ MSGBOX_ID msg_src = bacmsg->origin;
+
+ // allocate message structure
+ msg_data = malloc(sizeof(MSG_DATA));
+ if (!msg_data) {
+ PRINT(ERROR, "Error: Could not allocate memory\n");
+ break;
+ }
+
+ print_msg(bacmsg);
+
+ if (is_network_msg(bacmsg)) {
+ buff_len = process_network_message(bacmsg, msg_data, &buff);
+ if (buff_len == 0) {
+ free_data(bacmsg->data);
+ break;
+ }
+ } else {
+ buff_len = process_msg(bacmsg, msg_data, &buff);
+ }
+
+ // if buff_len
+ // >0 - form new message and send
+ // =-1 - try to find next router
+ // other value - discard message
+
+ if (buff_len > 0) {
+ // form new message
+ msg_data->pdu = buff;
+ msg_data->pdu_len = buff_len;
+ msg_storage.origin = head->main_id;
+ msg_storage.type = DATA;
+ msg_storage.data = msg_data;
+
+ print_msg(bacmsg);
+
+ if (is_network_msg(bacmsg)) {
+ msg_data->ref_count = 1;
+ send_to_msgbox(msg_src, &msg_storage);
+ } else if (msg_data->dest.net != BACNET_BROADCAST_NETWORK) {
+ msg_data->ref_count = 1;
+ port = find_dnet(msg_data->dest.net, &msg_data->dest);
+ send_to_msgbox(port->port_id, &msg_storage);
+ } else {
+ port = head;
+ msg_data->ref_count = port_count - 1;
+ while (port != NULL) {
+ if (port->port_id == msg_src || port->state == FINISHED) {
+ port = port->next;
+ continue;
+ }
+ send_to_msgbox(port->port_id, &msg_storage);
+ port = port->next;
+ }
+ }
+ } else if (buff_len == -1) {
+ uint16_t net = msg_data->dest.net; // NET to find
+ PRINT(INFO, "Searching NET...\n");
+ send_network_message(NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK, msg_data, &buff, &net);
+ } else {
+ // if invalid message send Reject-Message-To-Network
+ PRINT(ERROR, "Error: Invalid message\n");
+ free_data(msg_data);
+ }
+ }
+ break;
+ case SERVICE:
+ default:
+ break;
+ }
+ }
+ }
+
+ return 0;
+
+}
+
+void print_help()
+{
+ printf("Usage: router [init_parameters]\n"
+ "\ninit_method:\n"
+ "-c, --config \n\tinitialize router with a configuration file (.cfg) located at \n"
+ "-D, --device [params]\n\tinitialize a device using an interface specified with\n\t[params]\n"
+ "\ninit_parameters:\n"
+ "-n, --network \n\tspecify device network number\n"
+ "-P, --port \n\tspecify udp port for BIP device\n"
+ "-m, --mac [max_master] [max_frames]\n\tspecify MSTP port parameters\n"
+ "-b, --baud \n\tspecify MSTP port baud rate\n"
+ "-p, --parity \n\tspecify MSTP port parity\n"
+ "-d, --databits <5|6|7|8>\n\tspecify MSTP port databits\n"
+ "-s, --stopbits <1|2>\n\tspecify MSTP port stopbits\n");
+}
+
+bool read_config(
+ char *filepath)
+{
+ config_t cfg;
+ config_setting_t *setting;
+ ROUTER_PORT *current = head;
+ int result, fd;
+
+ config_init(&cfg);
+
+ // open configuration file
+ if (!config_read_file(&cfg, filepath)) {
+ PRINT(ERROR, "Config file error: %d - %s\n", config_error_line(&cfg), config_error_text(&cfg));
+ config_destroy(&cfg);
+ return false;
+ }
+
+ // get router "port" count
+ setting = config_lookup(&cfg, "ports");
+ if (setting != NULL) {
+ int count = config_setting_length(setting);
+ int i;
+
+ // lookup and initialize router "port" parameters
+ for (i = 0; i < count; i++) {
+ const char *dev_type;
+ const char *iface;
+ long int param;
+ const char *str_param;
+ config_setting_t *port = config_setting_get_elem(setting, i);
+
+ // create new list node to store port information
+ if (head == NULL) {
+ head = (ROUTER_PORT*)malloc(sizeof(ROUTER_PORT));
+ head->next = NULL;
+ current = head;
+ } else {
+ ROUTER_PORT *tmp = current;
+ current = current->next;
+ current = (ROUTER_PORT*)malloc(sizeof(ROUTER_PORT));
+ current->next = NULL;
+ tmp->next = current;
+ }
+
+ port_count++;
+ config_setting_lookup_string(port, "device_type", &dev_type);
+ printf("dev_type = %s\r\n", dev_type);
+ if (strcmp(dev_type, "bip") == 0) {
+ current->type = BIP;
+
+ result = config_setting_lookup_string(port, "device", &iface);
+ if (result) {
+ current->iface = (char*)malloc((strlen(iface) + 1)*sizeof(char));
+ strcpy(current->iface, iface);
+
+ // check if interface is valid
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd) {
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, current->iface, sizeof(ifr.ifr_name)-1);
+ result = ioctl(fd, SIOCGIFADDR, &ifr);
+ if (result != -1) {
+ close(fd);
+ } else {
+ PRINT(ERROR, "Error: Invalid interface for BIP device\n");
+ return false;
+ }
+ }
+ } else {
+ current->iface = "eth0";
+ }
+
+ result = config_setting_lookup_int(port, "port", (int*)¶m);
+ if (result) {
+ current->params.bip_params.port = param;
+ } else {
+ current->params.bip_params.port = 0xBAC0;
+ }
+ result = config_setting_lookup_int(port, "network", (int*)¶m);
+ if (result) {
+ current->route_info.net = param;
+ } else {
+ current->route_info.net = get_next_free_dnet();
+ }
+
+ } else if (strcmp(dev_type, "mstp") == 0) {
+ current->type = MSTP;
+
+ result = config_setting_lookup_string(port, "device", &iface);
+ if (result) {
+ current->iface = (char*)malloc((strlen(iface) + 1)*sizeof(char));
+ strcpy(current->iface, iface);
+
+ // check if interface is valid
+ fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
+ if (fd != -1) {
+ close(fd);
+ } else {
+ PRINT(ERROR, "Error: Invalid interface for MSTP device\n");
+ return false;
+ }
+ } else {
+ current->iface = "/dev/ttyS0";
+ }
+ result = config_setting_lookup_int(port, "mac", (int*)¶m);
+ if (result) {
+ current->route_info.mac[0] = param;
+ current->route_info.mac_len = 1;
+ } else {
+ current->route_info.mac[0] = 127;
+ current->route_info.mac_len = 1;
+ }
+ result = config_setting_lookup_int(port, "max_master", (int*)¶m);
+ if (result) {
+ current->params.mstp_params.max_master = param;
+ } else {
+ current->params.mstp_params.max_master = 127;
+ }
+ result = config_setting_lookup_int(port, "max_frames", (int*)¶m);
+ if (result) {
+ current->params.mstp_params.max_frames = param;
+ } else {
+ current->params.mstp_params.max_frames = 1;
+ }
+ result = config_setting_lookup_int(port, "baud", (int*)¶m);
+ if (result) {
+ current->params.mstp_params.baudrate = param;
+ } else {
+ current->params.mstp_params.baudrate = 9600;
+ }
+ result = config_setting_lookup_string(port, "parity", &str_param);
+ if (result) {
+ switch(str_param[0]){
+ case 'E':
+ current->params.mstp_params.parity = PARITY_EVEN;
+ break;
+ case 'O':
+ current->params.mstp_params.parity = PARITY_ODD;
+ break;
+ default:
+ current->params.mstp_params.parity = PARITY_NONE;
+ break;
+ }
+ } else {
+ current->params.mstp_params.parity = PARITY_NONE;
+ }
+ result = config_setting_lookup_int(port, "databits", (int*)¶m);
+ if (result && param >= 5 && param <= 8) {
+ current->params.mstp_params.databits = param;
+ } else {
+ current->params.mstp_params.databits = 8;
+ }
+ result = config_setting_lookup_int(port, "stopbits", (int*)¶m);
+ if (result && param >= 1 && param <= 2) {
+ current->params.mstp_params.stopbits = param;
+ } else {
+ current->params.mstp_params.stopbits = 1;
+ }
+ result = config_setting_lookup_int(port, "network", (int*)¶m);
+ if (result) {
+ current->route_info.net = param;
+ } else {
+ current->route_info.net = get_next_free_dnet();
+ }
+
+ } else {
+ PRINT(ERROR, "Error: %s unsuported\n", dev_type);
+ return false;
+ }
+ }
+ }
+ else {
+ config_destroy(&cfg);
+ return false;
+ }
+
+ config_destroy(&cfg);
+ printf("cmd file parse success\r\n");
+ return true;
+}
+
+bool parse_cmd(
+ int argc,
+ char *argv[])
+{
+ const char *optString = "hc:D:";
+ const char *bipString = "p:n:D:";
+ const char *mstpString = "m:b:p:d:s:n:D:";
+ const struct option Options[] = {
+ { "config", required_argument, NULL, 'c' },
+ { "device", required_argument, NULL, 'D' },
+ { "network", required_argument, NULL, 'n' },
+ { "port", required_argument, NULL, 'P' },
+ { "mac", required_argument, NULL, 'm' },
+ { "baud", required_argument, NULL, 'b' },
+ { "parity", required_argument, NULL, 'p' },
+ { "databits", required_argument, NULL, 'd' },
+ { "stopbits", required_argument, NULL, 's' },
+ { "help", no_argument, NULL, 'h' },
+ { NULL, no_argument, NULL, 0 },
+ };
+
+ int opt, dev_opt, index, result, fd;
+ ROUTER_PORT *current = head;
+
+ if (argc < 2)
+ print_help();
+
+ // begin checking cmd parameters
+ opt = getopt_long(argc, argv, optString, Options, &index);
+ printf("opt = %c\r\n", opt);
+ while (opt != -1) {
+ switch(opt) {
+ case 'h':
+ print_help();
+ return false;
+ break;
+ case 'c':
+ return read_config(optarg);
+ break;
+ case 'D':
+
+ // create new list node to store port information
+ if (head == NULL) {
+ head = (ROUTER_PORT*)malloc(sizeof(ROUTER_PORT));
+ head->next = NULL;
+ current = head;
+ } else {
+ ROUTER_PORT *tmp = current;
+ current = current->next;
+ current = (ROUTER_PORT*)malloc(sizeof(ROUTER_PORT));
+ current->next = NULL;
+ tmp->next = current;
+ }
+
+ port_count++;
+ if (strcmp(optarg, "bip") == 0) {
+ current->type = BIP;
+
+ if (optind < argc && argv[optind][0] != '-') {
+ current->iface = argv[optind];
+ } else {
+ current->iface = "eth0";
+ }
+
+ // setup default parameters
+ current->params.bip_params.port = 0xBAC0; // 47808
+ current->route_info.net = get_next_free_dnet();
+
+ // check if interface is valid
+ fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd) {
+ struct ifreq ifr;
+ strncpy(ifr.ifr_name, current->iface, sizeof(ifr.ifr_name)-1);
+ result = ioctl(fd, SIOCGIFADDR, &ifr);
+ if (result != -1) {
+ close(fd);
+ } else {
+ PRINT(ERROR, "Error: Invalid interface for BIP device \n");
+ return false;
+ }
+ }
+
+ dev_opt = getopt_long(argc, argv, bipString, Options, &index);
+ while (dev_opt != -1 && dev_opt != 'd') {
+ switch(dev_opt) {
+ case 'P':
+ result = atoi(optarg);
+ if (result) {
+ current->params.bip_params.port = (uint16_t)result;
+ } else {
+ current->params.bip_params.port = 0xBAC0; // 47808
+ }
+ break;
+ case 'n':
+ result = atoi(optarg);
+ if (result) {
+ current->route_info.net = (uint16_t)result;
+ } else {
+ current->route_info.net = port_count;
+ }
+ break;
+ }
+ dev_opt = getopt_long(argc, argv, bipString, Options, &index);
+ }
+ opt = dev_opt;
+ } else if (strcmp(optarg, "mstp") == 0) {
+ current->type = MSTP;
+
+ if (optind < argc && argv[optind][0] != '-') {
+ current->iface = argv[optind];
+ } else {
+ current->iface = "/dev/ttyS0";
+ }
+
+ // check if interface is valid
+ fd = open(current->iface, O_NOCTTY | O_NONBLOCK);
+ if (fd != -1) {
+ close(fd);
+ } else {
+ PRINT(ERROR, "Error: Invalid interface for MSTP device\n");
+ return false;
+ }
+
+ // setup default parameters
+ current->route_info.mac[0] = 127;
+ current->route_info.mac_len = 1;
+ current->params.mstp_params.max_master = 127;
+ current->params.mstp_params.max_frames = 1;
+ current->params.mstp_params.baudrate = 9600;
+ current->params.mstp_params.parity = PARITY_NONE;
+ current->params.mstp_params.databits = 8;
+ current->params.mstp_params.stopbits = 1;
+ current->route_info.net = get_next_free_dnet();
+
+ dev_opt = getopt_long(argc, argv, mstpString, Options, &index);
+ while (dev_opt != -1 && dev_opt != 'D') {
+ switch(dev_opt) {
+ case 'm':
+ result = atoi(optarg);
+ if (result) {
+ current->route_info.mac[0] = (uint8_t)result;
+ }
+ if (argv[optind][0] != '-') {
+ current->params.mstp_params.max_master = (uint8_t)atoi(argv[optind]);
+ if (current->params.mstp_params.max_master < current->route_info.mac[0])
+ current->params.mstp_params.max_master = current->route_info.mac[0];
+
+ if (argv[optind+1][0] != '-') {
+ current->params.mstp_params.max_frames = (uint8_t)atoi(argv[optind+1]);
+ }
+ }
+ break;
+ case 'b':
+ result = atoi(optarg);
+ if (result) {
+ current->params.mstp_params.baudrate = (uint32_t)result;
+ }
+ break;
+ case 'p':
+ switch(optarg[0]){
+ case 'E':
+ current->params.mstp_params.parity = PARITY_EVEN;
+ break;
+ case 'O':
+ current->params.mstp_params.parity = PARITY_ODD;
+ break;
+ default:
+ current->params.mstp_params.parity = PARITY_NONE;
+ break;
+ }
+ break;
+ case 'd':
+ result = atoi(optarg);
+ if (result >= 5 && result <= 8) {
+ current->params.mstp_params.databits = (uint8_t)result;
+ }
+ break;
+ case 's':
+ result = atoi(optarg);
+ if (result >= 1 && result <= 2) {
+ current->params.mstp_params.stopbits = (uint8_t)result;
+ }
+ break;
+ case 'n':
+ result = atoi(optarg);
+ if (result) {
+ current->route_info.net = (uint16_t)result;
+ }
+ break;
+ }
+ dev_opt = getopt_long(argc, argv, mstpString, Options, &index);
+ }
+ opt = dev_opt;
+ } else {
+ PRINT(ERROR, "Error: %s unknown\n", optarg);
+ return false;
+ }
+ break;
+ }
+ }
+ return true;
+}
+
+void init_port_threads(
+ ROUTER_PORT* port_list) {
+ ROUTER_PORT *port = port_list;
+ pthread_t * thread;
+
+ while (port != NULL) {
+ switch (port->type) {
+ case BIP:
+ port->func = &dl_ip_thread;
+ break;
+ case MSTP:
+ port->func = &dl_mstp_thread;
+ break;
+ }
+
+ port->state = INIT;
+ thread = (pthread_t *)malloc(sizeof(pthread_t));
+ pthread_create(thread,
+ NULL,
+ port->func,
+ port);
+
+ pthread_detach(*thread); // for proper thread termination
+
+ port = port->next;
+ }
+}
+
+bool init_router() {
+ MSGBOX_ID msgboxid;
+ ROUTER_PORT *port;
+
+ msgboxid = create_msgbox();
+ if (msgboxid == INVALID_MSGBOX_ID)
+ return false;
+
+ port = head;
+ // add main message box id to all ports
+ while (port != NULL) {
+ port->main_id = msgboxid;
+ port = port->next;
+ }
+
+ init_port_threads(head);
+
+ // wait for port initialization
+ port = head;
+ while (port != NULL) {
+ if (port->state == RUNNING) {
+ port = port->next;
+ continue;
+ } else if (port->state == INIT_FAILED) {
+ PRINT(ERROR, "Error: Failed to initialize %s\n", port->iface);
+ return false;
+ } else {
+ PRINT(INFO, "Initializing...\n");
+ sleep(1);
+ continue;
+ }
+ }
+
+ return true;
+}
+
+void cleanup() {
+ ROUTER_PORT *port;
+ BACMSG msg;
+
+ if (head == NULL)
+ return;
+
+ msg.origin = head->main_id;
+ msg.type = SERVICE;
+ msg.subtype = SHUTDOWN;
+
+ del_msgbox(head->main_id); // close routers message box
+
+ // send shutdown message to all router ports
+ port = head;
+ while (port != NULL) {
+ if (port->state == RUNNING)
+ send_to_msgbox(port->port_id, &msg);
+ port = port->next;
+ }
+
+ port = head;
+ while (port != NULL) {
+ if (port->state == FINISHED) {
+ cleanup_dnets(port->route_info.dnets);
+ port = port->next;
+ free(head->iface);
+ free(head);
+ head = port;
+ }
+ }
+
+ pthread_mutex_destroy(&msg_lock);
+}
+
+void print_msg(BACMSG *msg) {
+ if (msg->type == DATA) {
+ int i;
+ MSG_DATA *data = (MSG_DATA*)msg->data;
+
+ if (data->pdu_len) {
+ PRINT(DEBUG, "Message PDU: ");
+ for (i = 0; i < data->pdu_len; i++)
+ PRINT(DEBUG, "%02X ",data->pdu[i]);
+ PRINT(DEBUG, "\n");
+ }
+ }
+}
+
+uint16_t process_msg(BACMSG *msg,
+ MSG_DATA *data,
+ uint8_t **buff) {
+
+ BACNET_ADDRESS addr;
+ BACNET_NPDU_DATA npdu_data;
+ ROUTER_PORT *srcport;
+ ROUTER_PORT *destport;
+ uint8_t npdu[MAX_NPDU];
+ int16_t buff_len = 0;
+ int apdu_offset;
+ int apdu_len;
+ int npdu_len;
+
+ memmove(data, msg->data, sizeof(MSG_DATA));
+
+ apdu_offset = npdu_decode(data->pdu, &data->dest, &addr, &npdu_data);
+ apdu_len = data->pdu_len - apdu_offset;
+
+ srcport = find_snet(msg->origin);
+ destport = find_dnet(data->dest.net, NULL);
+ assert(srcport);
+
+ if (srcport && destport) {
+ data->src.net = srcport->route_info.net;
+
+ // if received from another router save real source address (not other router source address)
+ if (addr.net > 0 && addr.net < BACNET_BROADCAST_NETWORK && data->src.net != addr.net)
+ memmove(&data->src, &addr, sizeof(BACNET_ADDRESS));
+
+ // encode both source and destination for broadcast and router-to-router communication
+ if (data->dest.net == BACNET_BROADCAST_NETWORK || destport->route_info.net != data->dest.net) {
+ npdu_len = npdu_encode_pdu(npdu, &data->dest, &data->src, &npdu_data);
+ }
+ else {
+ npdu_len = npdu_encode_pdu(npdu, NULL, &data->src, &npdu_data);
+ }
+
+ buff_len = npdu_len + data->pdu_len - apdu_offset;
+
+ *buff = (uint8_t*)malloc(buff_len);
+ memmove(*buff, npdu, npdu_len); // copy newly formed NPDU
+ memmove(*buff+npdu_len, &data->pdu[apdu_offset], apdu_len); // copy APDU
+
+ } else {
+ // request net search
+ return -1;
+ }
+
+ // delete received message
+ free_data((MSG_DATA*)msg->data);
+
+ return buff_len;
+}
+
+int kbhit()
+{
+ static const int STDIN = 0;
+ static bool initialized = false;
+
+ if (! initialized) {
+ // use termios to turn off line buffering
+ struct termios term;
+ tcgetattr(STDIN, &term);
+ term.c_lflag &= ~ICANON;
+ tcsetattr(STDIN, TCSANOW, &term);
+ setbuf(stdin, NULL);
+ initialized = true;
+ }
+
+ int bytesWaiting;
+ ioctl(STDIN, FIONREAD, &bytesWaiting);
+ return bytesWaiting;
+}
+
+bool is_network_msg(BACMSG *msg) {
+
+ uint8_t control_byte; // NPDU control byte
+ MSG_DATA *data = (MSG_DATA*)msg->data;
+
+ control_byte = data->pdu[1];
+
+ return control_byte & 0x80; // check 7th bit
+}
+
+uint16_t get_next_free_dnet() {
+
+ ROUTER_PORT * port = head;
+ uint16_t i = 1;
+ while(port) {
+ if(port->route_info.net == i) {
+ port = head;
+ i++;
+ continue;
+ }
+
+ port = port->next;
+ }
+ return i;
+}
+
diff --git a/bacnet-stack/demo/router/msgqueue.c b/bacnet-stack/demo/router/msgqueue.c
new file mode 100644
index 00000000..3aa738a1
--- /dev/null
+++ b/bacnet-stack/demo/router/msgqueue.c
@@ -0,0 +1,95 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include
+#include
+#include "msgqueue.h"
+
+pthread_mutex_t msg_lock = PTHREAD_MUTEX_INITIALIZER;
+
+MSGBOX_ID create_msgbox() {
+ MSGBOX_ID msgboxid;
+
+ msgboxid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
+ if (msgboxid == INVALID_MSGBOX_ID) {
+ return INVALID_MSGBOX_ID;
+ }
+
+ return msgboxid;
+}
+
+bool send_to_msgbox(
+ MSGBOX_ID dest,
+ BACMSG *msg) {
+
+ int err;
+
+ err = msgsnd(dest, msg, sizeof(BACMSG), 0);
+ if (err) {
+ return false;
+ }
+ return true;
+}
+
+BACMSG* recv_from_msgbox(
+ MSGBOX_ID src,
+ BACMSG *msg) {
+
+ int recv_bytes;
+
+ recv_bytes = msgrcv(src, msg, sizeof(BACMSG), 0, IPC_NOWAIT);
+ if (recv_bytes > 0) {
+ return msg;
+ } else {
+ return NULL;
+ }
+}
+
+void del_msgbox(
+ MSGBOX_ID msgboxid) {
+
+ if (msgboxid == INVALID_MSGBOX_ID)
+ return;
+ else
+ msgctl(msgboxid, IPC_RMID, NULL);
+}
+
+void free_data(
+ MSG_DATA *data) {
+
+ if (data->pdu) {
+ free(data->pdu);
+ data->pdu = NULL;
+ }
+ if (data) {
+ free(data);
+ data = NULL;
+ }
+}
+
+void check_data(
+ MSG_DATA *data) {
+
+ // lock and decrement messages reference count
+ pthread_mutex_lock(&msg_lock);
+ if (--data->ref_count == 0) {
+ free_data(data);
+ }
+ pthread_mutex_unlock(&msg_lock);
+}
diff --git a/bacnet-stack/demo/router/msgqueue.h b/bacnet-stack/demo/router/msgqueue.h
new file mode 100644
index 00000000..cdf97b80
--- /dev/null
+++ b/bacnet-stack/demo/router/msgqueue.h
@@ -0,0 +1,85 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#ifndef MSGQUEUE_H
+#define MSGQUEUE_H
+
+#include
+#include
+#include
+#include
+#include
+#include "bacdef.h"
+#include "npdu.h"
+
+extern pthread_mutex_t msg_lock;
+
+#define INVALID_MSGBOX_ID -1
+
+typedef int MSGBOX_ID;
+
+typedef enum {
+ DATA,
+ SERVICE
+} MSGTYPE;
+
+typedef enum {
+ SHUTDOWN,
+ CHG_IP,
+ CHG_MAC
+} MSGSUBTYPE;
+
+typedef struct _message {
+ MSGBOX_ID origin;
+ MSGTYPE type;
+ MSGSUBTYPE subtype;
+ void *data;
+ // add timestamp
+} BACMSG;
+
+// specific message type data structures
+typedef struct _msg_data {
+ BACNET_ADDRESS dest;
+ BACNET_ADDRESS src;
+ uint8_t *pdu;
+ uint16_t pdu_len;
+ uint8_t ref_count;
+} MSG_DATA;
+
+MSGBOX_ID create_msgbox();
+
+// returns sent byte count
+bool send_to_msgbox(
+ MSGBOX_ID dest,
+ BACMSG *msg);
+
+// returns received message
+BACMSG* recv_from_msgbox(
+ MSGBOX_ID src,
+ BACMSG *msg);
+
+void del_msgbox(
+ MSGBOX_ID msgboxid);
+
+// free message data structure
+void free_data(
+ MSG_DATA *data);
+
+// check message reference counter and delete data if needed
+void check_data(
+ MSG_DATA *data);
+
+#endif /* end of MSGQUEUE_H */
diff --git a/bacnet-stack/demo/router/mstpmodule.c b/bacnet-stack/demo/router/mstpmodule.c
new file mode 100644
index 00000000..87bce959
--- /dev/null
+++ b/bacnet-stack/demo/router/mstpmodule.c
@@ -0,0 +1,168 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include "mstpmodule.h"
+#include "bacint.h"
+#include "dlmstp_linux.h"
+#include
+
+#define MSTP_THREAD_PRINT_ENABLED
+#ifdef MSTP_THREAD_PRINT_ENABLED
+#define mstp_thread_debug(...) fprintf(stderr, __VA_ARGS__)
+#else
+#define mstp_thread_debug(...)
+#endif
+
+void* dl_mstp_thread(void* pArgs) {
+
+ ROUTER_PORT *port = (ROUTER_PORT*)pArgs;
+ struct mstp_port_struct_t mstp_port = {(MSTP_RECEIVE_STATE)0};
+ volatile SHARED_MSTP_DATA shared_port_data = {0};
+ uint16_t pdu_len;
+ uint8_t shutdown = 0;
+
+ shared_port_data.Treply_timeout = 260;
+ shared_port_data.MSTP_Packets = 0;
+ shared_port_data.Tusage_timeout = 50;
+ shared_port_data.RS485_Handle = -1;
+ shared_port_data.RS485_Baud = B38400;
+ shared_port_data.RS485MOD = 0;
+
+ switch(port->params.mstp_params.databits){
+ case 5:
+ shared_port_data.RS485MOD = CS5;
+ break;
+ case 6:
+ shared_port_data.RS485MOD = CS6;
+ break;
+ case 7:
+ shared_port_data.RS485MOD = CS7;
+ break;
+ default:
+ shared_port_data.RS485MOD = CS8;
+ break;
+ }
+
+ switch(port->params.mstp_params.parity){
+ case PARITY_EVEN:
+ shared_port_data.RS485MOD |= PARENB;
+ break;
+ case PARITY_ODD:
+ shared_port_data.RS485MOD |= PARENB | PARODD;
+ break;
+ default:
+ break;
+ }
+
+ if(port->params.mstp_params.stopbits == 2)
+ shared_port_data.RS485MOD |= CSTOPB;
+
+ mstp_port.UserData = (void*) &shared_port_data;
+ dlmstp_set_baud_rate(&mstp_port, port->params.mstp_params.baudrate);
+ dlmstp_set_mac_address(&mstp_port, port->route_info.mac[0]);
+ dlmstp_set_max_info_frames(&mstp_port, port->params.mstp_params.max_frames);
+ dlmstp_set_max_master(&mstp_port, port->params.mstp_params.max_master);
+ if(!dlmstp_init(&mstp_port, port->iface))
+ printf("MSTP %s init failed. Stop.\n", port->iface);
+
+ port->port_id = create_msgbox();
+ if (port->port_id == INVALID_MSGBOX_ID) {
+ port->state = INIT_FAILED;
+ return NULL;
+ }
+
+ port->state = RUNNING;
+
+ while (!shutdown) {
+ // message loop
+ BACMSG msg_storage, * bacmsg;
+ MSG_DATA * msg_data;
+
+ bacmsg = recv_from_msgbox(port->port_id, &msg_storage);
+
+ if(bacmsg)
+ {
+ switch(bacmsg->type)
+ {
+ case DATA:
+ msg_data = (MSG_DATA*) bacmsg->data;
+
+ if(msg_data->dest.net == BACNET_BROADCAST_NETWORK)
+ {
+ dlmstp_get_broadcast_address(&(msg_data->dest));
+ }
+ else
+ {
+ msg_data->dest.mac[0] = msg_data->dest.adr[0];
+ msg_data->dest.mac_len = 1;
+ }
+
+ dlmstp_send_pdu(&mstp_port, &(msg_data->dest), msg_data->pdu, msg_data->pdu_len);
+
+ check_data(msg_data);
+
+
+ break;
+ case SERVICE:
+ switch(bacmsg->subtype) {
+ case SHUTDOWN:
+ shutdown = 1;
+ break;
+ default:
+ break;
+ }
+ break;
+ default:
+ continue;
+ break;
+ }
+ }
+ else
+ {
+ pdu_len = dlmstp_receive(&mstp_port, NULL, NULL, 0, 1000);
+
+ if(pdu_len > 0)
+ {
+ msg_data = (MSG_DATA*)malloc(sizeof(MSG_DATA));
+ memmove(&(msg_data->src), (const void*)&(shared_port_data.Receive_Packet.address), sizeof(shared_port_data.Receive_Packet.address));
+ msg_data->src.adr[0] = msg_data->src.mac[0];
+ msg_data->src.len = 1;
+ msg_data->pdu = (uint8_t*)malloc(pdu_len);
+ memmove(msg_data->pdu, (const void*)&(shared_port_data.Receive_Packet.pdu), pdu_len);
+ msg_data->pdu_len = pdu_len;
+
+ msg_storage.type = DATA;
+ msg_storage.subtype = (MSGSUBTYPE)0;
+ msg_storage.origin = port->port_id;
+ msg_storage.data = msg_data;
+
+ if(!send_to_msgbox(port->main_id, &msg_storage))
+ {
+ free_data(msg_data);
+ }
+ }
+ }
+ }
+
+ dlmstp_cleanup(&mstp_port);
+ port->state = FINISHED;
+
+ return NULL;
+
+}
diff --git a/bacnet-stack/demo/router/mstpmodule.h b/bacnet-stack/demo/router/mstpmodule.h
new file mode 100644
index 00000000..44a3ff85
--- /dev/null
+++ b/bacnet-stack/demo/router/mstpmodule.h
@@ -0,0 +1,25 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#ifndef MSTPMODULE_H
+#define MSTPMODULE_H
+
+#include "portthread.h"
+
+void* dl_mstp_thread(
+ void* pArgs);
+
+#endif /* end of MSTPMODULE_H */
diff --git a/bacnet-stack/demo/router/network_layer.c b/bacnet-stack/demo/router/network_layer.c
new file mode 100644
index 00000000..1648fc55
--- /dev/null
+++ b/bacnet-stack/demo/router/network_layer.c
@@ -0,0 +1,311 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include "network_layer.h"
+#include "bacint.h"
+
+uint16_t process_network_message(
+ BACMSG *msg,
+ MSG_DATA *data,
+ uint8_t **buff) {
+
+ BACNET_NPDU_DATA npdu_data;
+ ROUTER_PORT *srcport;
+ ROUTER_PORT *destport;
+ uint16_t net;
+ uint8_t error_code;
+ int16_t buff_len = 0;
+ int apdu_offset;
+ int apdu_len;
+
+ memmove(data, msg->data, sizeof(MSG_DATA));
+
+ apdu_offset = npdu_decode(data->pdu, &data->dest, NULL, &npdu_data);
+ apdu_len = data->pdu_len - apdu_offset;
+
+ srcport = find_snet(msg->origin);
+ data->src.net = srcport->route_info.net;
+
+ switch (npdu_data.network_message_type) {
+
+ case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
+ PRINT(INFO, "Recieved Who-Is-Router-To-Network message\n");
+ if (apdu_len) {
+ // if NET specified
+ decode_unsigned16(&data->pdu[apdu_offset], &net);
+ if (srcport->route_info.net == net) {
+ PRINT(INFO, "Message discarded: NET directly connected\n");
+ return -2;
+ }
+
+ destport = find_dnet(net, NULL); // see if NET can be reached
+ if (destport) {
+ // if TRUE send reply
+ PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
+ buff_len = create_network_message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK,
+ data, buff, &net);
+ } else {
+ data->dest.net = net; // NET to look for
+ return -1; // else initiate NET search procedure
+ }
+ } else {
+ // if NET is omitted (message sent with -1)
+ PRINT(INFO, "Sending I-Am-Router-To-Network message\n");
+ buff_len = create_network_message(NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK,
+ data, buff, NULL);
+ }
+
+ break;
+
+ case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
+ {
+ PRINT(INFO, "Recieved I-Am-Router-To-Network message\n");
+ int net_count = apdu_len / 2;
+ int i;
+ for (i = 0; i < net_count; i++) {
+ decode_unsigned16(&data->pdu[apdu_offset+2*i], &net); // decode received NET values
+ add_dnet(&srcport->route_info, net, data->src); // and update routing table
+ }
+ break;
+ }
+ case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
+ {
+ // first octet of the message contains rejection reason
+ // next two octets contain NET (can be decoded for additional info on error)
+ error_code = data->pdu[apdu_offset];
+ switch (error_code) {
+ case 0:
+ PRINT(ERROR, "Error!\n");
+ break;
+ case 1:
+ PRINT(ERROR, "Error: Network unreachable\n");
+ break;
+ case 2:
+ PRINT(ERROR, "Error: Network is busy\n");
+ break;
+ case 3:
+ PRINT(ERROR, "Error: Unknown network message type\n");
+ break;
+ case 4:
+ PRINT(ERROR, "Error: Message too long\n");
+ break;
+ }
+ break;
+ }
+ case NETWORK_MESSAGE_INIT_RT_TABLE:
+ PRINT(INFO, "Recieved Initialize-Routing-Table message\n");
+ if (data->pdu[apdu_offset] > 0) {
+ int net_count = data->pdu[apdu_offset];
+ while (net_count--) {
+ int i = 1;
+ decode_unsigned16(&data->pdu[apdu_offset+i], &net); // decode received NET values
+ add_dnet(&srcport->route_info, net, data->src); // and update routing table
+ if (data->pdu[apdu_offset+i+3] > 0) // find next NET value
+ i = data->pdu[apdu_offset+i+3] + 4;
+ else
+ i = i + 4;
+ }
+ buff_len = create_network_message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, NULL);
+ } else
+ buff_len = create_network_message(NETWORK_MESSAGE_INIT_RT_TABLE_ACK, data, buff, &buff);
+ break;
+
+ case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
+ PRINT(INFO, "Recieved Initialize-Routing-Table-Ack message\n");
+ if (data->pdu[apdu_offset] > 0) {
+ int net_count = data->pdu[apdu_offset];
+ while (net_count--) {
+ int i = 1;
+ decode_unsigned16(&data->pdu[apdu_offset+i], &net); // decode received NET values
+ add_dnet(&srcport->route_info, net, data->src); // and update routing table
+ if (data->pdu[apdu_offset+i+3] > 0) // find next NET value
+ i = data->pdu[apdu_offset+i+3] + 4;
+ else
+ i = i + 4;
+ }
+ }
+ break;
+
+ case NETWORK_MESSAGE_INVALID:
+ case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
+ case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
+ case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
+ case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
+ case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
+ // hell if I know what to do with these messages
+ break;
+
+ default:
+ PRINT(ERROR, "Error: Message unsupported\n");
+ break;
+ }
+
+ return buff_len;
+}
+
+uint16_t create_network_message(
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ MSG_DATA *data,
+ uint8_t **buff,
+ void *val) {
+
+ int16_t buff_len;
+ bool data_expecting_reply = false;
+ BACNET_NPDU_DATA npdu_data;
+
+ if (network_message_type == NETWORK_MESSAGE_INIT_RT_TABLE)
+ data_expecting_reply = true;
+ init_npdu(&npdu_data, network_message_type, data_expecting_reply);
+
+ *buff = (uint8_t*)malloc(128); // resolve different length
+
+ // manual destination setup for Init-RT-Table-Ack message
+ data->dest.net = BACNET_BROADCAST_NETWORK;
+ buff_len = npdu_encode_pdu(*buff, &data->dest, NULL, &npdu_data);
+
+ switch (network_message_type) {
+
+ case NETWORK_MESSAGE_WHO_IS_ROUTER_TO_NETWORK:
+ if (val != NULL) {
+ uint8_t * valptr = (uint8_t*)val;
+ uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
+ buff_len += encode_unsigned16(*buff+buff_len, val16);
+ }
+ break;
+
+ case NETWORK_MESSAGE_I_AM_ROUTER_TO_NETWORK:
+ if (val != NULL) {
+ uint8_t * valptr = (uint8_t*)val;
+ uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
+ buff_len += encode_unsigned16(*buff+buff_len, val16);
+ }
+ else {
+ ROUTER_PORT *port = head;
+ DNET *dnet;
+ while (port != NULL) {
+ if (port->route_info.net != data->src.net) {
+ buff_len += encode_unsigned16(*buff+buff_len, port->route_info.net);
+ dnet = port->route_info.dnets;
+ while (dnet != NULL) {
+ buff_len += encode_unsigned16(*buff+buff_len, dnet->net);
+ dnet = dnet->next;
+ }
+ port = port->next;
+ } else {
+ dnet = port->route_info.dnets;
+ while (dnet != NULL) {
+ buff_len += encode_unsigned16(*buff+buff_len, dnet->net);
+ dnet = dnet->next;
+ }
+ port = port->next;
+ }
+ }
+ }
+ break;
+
+ case NETWORK_MESSAGE_REJECT_MESSAGE_TO_NETWORK:
+ {
+ uint8_t * valptr = (uint8_t*)val;
+ uint16_t val16 = (valptr[0]) + (valptr[1] << 8);
+ buff_len += encode_unsigned16(*buff + buff_len, val16);
+ break;
+ }
+ case NETWORK_MESSAGE_INIT_RT_TABLE:
+ case NETWORK_MESSAGE_INIT_RT_TABLE_ACK:
+ if ((uint8_t*)val) {
+ (*buff)[buff_len++] = (uint8_t) port_count;
+
+ if (port_count > 0) {
+ ROUTER_PORT *port = head;
+ uint8_t portID = 1;
+
+ while (port != NULL) {
+ buff_len += encode_unsigned16(*buff+buff_len, port->route_info.net);
+ (*buff)[buff_len++] = portID++;
+ (*buff)[buff_len++] = 0;
+ port = port->next;
+ }
+ }
+ } else
+ (*buff)[buff_len++] = (uint8_t) 0;
+ break;
+
+ case NETWORK_MESSAGE_INVALID:
+ case NETWORK_MESSAGE_I_COULD_BE_ROUTER_TO_NETWORK:
+ case NETWORK_MESSAGE_ROUTER_BUSY_TO_NETWORK:
+ case NETWORK_MESSAGE_ROUTER_AVAILABLE_TO_NETWORK:
+ case NETWORK_MESSAGE_ESTABLISH_CONNECTION_TO_NETWORK:
+ case NETWORK_MESSAGE_DISCONNECT_CONNECTION_TO_NETWORK:
+ // hell if I know what to do with these messages
+ break;
+ }
+
+ return buff_len;
+}
+
+void send_network_message(
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ MSG_DATA *data,
+ uint8_t **buff,
+ void *val) {
+
+ BACMSG msg;
+ ROUTER_PORT *port = head;
+ int16_t buff_len;
+
+ if (!data) {
+ data = (MSG_DATA*)malloc(sizeof(MSG_DATA));
+ data->dest.net = BACNET_BROADCAST_NETWORK;
+ }
+
+ buff_len = create_network_message(network_message_type, data, buff, val);
+
+ // form network message
+ data->pdu = *buff;
+ data->pdu_len = buff_len;
+ msg.origin = head->main_id;
+ msg.type = DATA;
+ msg.data = data;
+
+ data->ref_count = port_count;
+ while (port != NULL) {
+ if (port->state == FINISHED) {
+ port = port->next;
+ continue;
+ }
+ send_to_msgbox(port->port_id, &msg);
+ port = port->next;
+ }
+}
+
+void init_npdu(
+ BACNET_NPDU_DATA *npdu_data,
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ bool data_expecting_reply) {
+
+ if (npdu_data) {
+ npdu_data->data_expecting_reply = data_expecting_reply;
+ npdu_data->protocol_version = BACNET_PROTOCOL_VERSION;
+ npdu_data->network_layer_message = true;
+ npdu_data->network_message_type = network_message_type;
+ npdu_data->vendor_id = 0;
+ npdu_data->priority = MESSAGE_PRIORITY_NORMAL;
+ npdu_data->hop_count = HOP_COUNT_DEFAULT;
+ }
+}
diff --git a/bacnet-stack/demo/router/network_layer.h b/bacnet-stack/demo/router/network_layer.h
new file mode 100644
index 00000000..a8ae428c
--- /dev/null
+++ b/bacnet-stack/demo/router/network_layer.h
@@ -0,0 +1,53 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#ifndef NETWORK_LAYER_H
+#define NETWORK_LAYER_H
+
+#include
+#include
+#include
+#include
+#include
+#include "bacenum.h"
+#include "bacdef.h"
+#include "npdu.h"
+#include "net.h"
+#include "portthread.h"
+
+uint16_t process_network_message(
+ BACMSG *msg,
+ MSG_DATA *data,
+ uint8_t **buff);
+
+uint16_t create_network_message(
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ MSG_DATA *data,
+ uint8_t **buff,
+ void *val);
+
+void send_network_message(
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ MSG_DATA *data,
+ uint8_t **buff,
+ void *val);
+
+void init_npdu(
+ BACNET_NPDU_DATA *npdu_data,
+ BACNET_NETWORK_MESSAGE_TYPE network_message_type,
+ bool data_expecting_reply);
+
+#endif /* end of NETWORK_LAYER_H */
diff --git a/bacnet-stack/demo/router/portthread.c b/bacnet-stack/demo/router/portthread.c
new file mode 100644
index 00000000..690b0b30
--- /dev/null
+++ b/bacnet-stack/demo/router/portthread.c
@@ -0,0 +1,116 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#include
+#include
+#include
+#include "portthread.h"
+
+ROUTER_PORT* find_snet(
+ MSGBOX_ID id) {
+
+ ROUTER_PORT *port = head;
+
+ while (port != NULL) {
+ if (port->port_id == id)
+ return port;
+ port = port->next;
+ }
+
+ return NULL;
+}
+
+ROUTER_PORT* find_dnet(
+ uint16_t net,
+ BACNET_ADDRESS *addr) {
+
+ ROUTER_PORT *port = head;
+ DNET *dnet;
+
+ // for broadcast messages no search is needed
+ if (net == BACNET_BROADCAST_NETWORK)
+ return port;
+
+ while(port != NULL) {
+
+ // check if DNET is directly connected to the router
+ if (net == port->route_info.net)
+ return port;
+
+ // else search router ports DNET list
+ else if (port->route_info.dnets) {
+ dnet = port->route_info.dnets;
+ while (dnet != NULL) {
+ if (net == dnet->net) {
+ if (addr) {
+ memmove(&addr->len, &dnet->mac_len, 1);
+ memmove(&addr->adr[0], &dnet->mac[0], MAX_MAC_LEN);
+ }
+ return port;
+ }
+ dnet = dnet->next;
+ }
+ }
+ port = port->next;
+ }
+
+ return NULL;
+}
+
+void add_dnet(
+ RT_ENTRY *route_info,
+ uint16_t net,
+ BACNET_ADDRESS addr) {
+
+ DNET *dnet = route_info->dnets;
+ DNET *tmp;
+
+ if (dnet == NULL) {
+ route_info->dnets = (DNET*)malloc(sizeof(DNET));
+ memmove(&route_info->dnets->mac_len, &addr.len, 1);
+ memmove(&route_info->dnets->mac[0], &addr.adr[0], MAX_MAC_LEN);
+ route_info->dnets->net = net;
+ route_info->dnets->state = true;
+ route_info->dnets->next = NULL;
+ } else {
+
+ while (dnet != NULL) {
+ if (dnet->net == net) // make sure NETs are not repeated
+ return;
+ tmp = dnet;
+ dnet = dnet->next;
+ }
+
+ dnet = (DNET*)malloc(sizeof(DNET));
+ memmove(&dnet->mac_len, &addr.len, 1);
+ memmove(&dnet->mac[0], &addr.adr[0], MAX_MAC_LEN);
+ dnet->net = net;
+ dnet->state = true;
+ dnet->next = NULL;
+ tmp->next = dnet;
+ }
+}
+
+void cleanup_dnets(
+ DNET *dnets) {
+
+ DNET *dnet = dnets;
+ while(dnet != NULL) {
+ dnet = dnet->next;
+ free(dnets);
+ dnets = dnet;
+ }
+}
diff --git a/bacnet-stack/demo/router/portthread.h b/bacnet-stack/demo/router/portthread.h
new file mode 100644
index 00000000..769fbaa6
--- /dev/null
+++ b/bacnet-stack/demo/router/portthread.h
@@ -0,0 +1,125 @@
+/*
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+ 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 3 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, see .
+*/
+#ifndef PORTTHREAD_H
+#define PORTTHREAD_H
+
+#include
+#include
+#include
+#include "msgqueue.h"
+#include "bacdef.h"
+#include "npdu.h"
+
+#define ERROR 1
+#define INFO 2
+#define DEBUG 3
+
+#define DEBUG_LEVEL 3
+#ifdef DEBUG_LEVEL
+#define PRINT(debug_level, ...) if(debug_level <= DEBUG_LEVEL) fprintf(stderr, __VA_ARGS__)
+#else
+#define PRINT(...)
+#endif
+
+typedef enum {
+ BIP = 1,
+ MSTP = 2
+} DL_TYPE;
+
+typedef enum {
+ INIT,
+ INIT_FAILED,
+ RUNNING,
+ FINISHED
+} PORT_STATE;
+
+// router port thread function
+typedef void* (*PORT_FUNC)(
+ void*);
+
+typedef enum {
+ PARITY_NONE,
+ PARITY_EVEN,
+ PARITY_ODD
+}PARITY;
+
+// port specific parameters
+typedef union _port_params {
+ struct {
+ uint16_t port;
+ } bip_params;
+ struct {
+ uint32_t baudrate;
+ PARITY parity;
+ uint8_t databits;
+ uint8_t stopbits;
+ uint8_t max_master;
+ uint8_t max_frames;
+ } mstp_params;
+} PORT_PARAMS;
+
+// list node for reacheble networks
+typedef struct _dnet {
+ uint8_t mac[MAX_MAC_LEN];
+ uint8_t mac_len;
+ uint16_t net;
+ bool state; // enabled or disabled
+ struct _dnet *next;
+} DNET;
+
+// information for routing table
+typedef struct _routing_table_entry {
+ uint8_t mac[MAX_MAC_LEN];
+ uint8_t mac_len;
+ uint16_t net;
+ DNET *dnets;
+} RT_ENTRY;
+
+typedef struct _port {
+ DL_TYPE type;
+ PORT_STATE state;
+ MSGBOX_ID main_id; // same for every router port
+ MSGBOX_ID port_id; // different for every router port
+ char *iface;
+ PORT_FUNC func;
+ RT_ENTRY route_info;
+ PORT_PARAMS params;
+ struct _port *next; // pointer to next list node
+} ROUTER_PORT;
+
+extern ROUTER_PORT *head;
+extern int port_count;
+
+// get recieving router port
+ROUTER_PORT* find_snet(
+ MSGBOX_ID id);
+
+// get sending router port
+ROUTER_PORT* find_dnet(
+ uint16_t net,
+ BACNET_ADDRESS *addr);
+
+// add reacheble network for specified router port
+void add_dnet(
+ RT_ENTRY *route_info,
+ uint16_t net,
+ BACNET_ADDRESS addr);
+
+void cleanup_dnets(
+ DNET *dnets);
+
+#endif /* end of PORTTHREAD_H */
diff --git a/bacnet-stack/demo/router/readme.txt b/bacnet-stack/demo/router/readme.txt
new file mode 100644
index 00000000..32cc667a
--- /dev/null
+++ b/bacnet-stack/demo/router/readme.txt
@@ -0,0 +1,138 @@
+-----------------------
+1. About
+-----------------------
+
+The Router connects two or more BACnet/IP and BACnet MS/TP networks.
+Number of netwoks is limited only by available hardware communication devices (or ports for Ethernet).
+
+-----------------------
+2. License
+-----------------------
+
+Copyright (C) 2012 Andriy Sukhynyuk, Vasyl Tkhir, Andriy Ivasiv
+
+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 3 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, see .
+
+
+-----------------------
+3. Build
+-----------------------
+
+1. Download, build and install libconfig C/C++ Configuration File Library from http://www.hyperrealm.com/libconfig
+2. Set variable "BACNET_PORT" in library root diredory Makefile to linux
+3. Run "make clean all" from library root directory
+
+-----------------------
+4. Router configuration
+-----------------------
+
+4.1. Configuration file format.
+
+//single line comment
+
+/*
+ multiline comment
+*/
+
+ports =
+(
+ //route_1
+ {
+ device_type = "";
+ //route specific arguments, see below
+ },
+
+ //route_2
+ {
+ device_type = "";
+ //route specific arguments, see below
+ },
+
+ //.....
+
+ //route_n
+ {
+ device_type = "";
+ //route specific arguments, see below
+ }
+);
+
+Note: - arguments are separeted with ';'
+ - routes are separeted with ','
+ - no ',' after the last route
+
+4.2. Configuration file arguments.
+
+Common arguments:
+ device_type - Describes a type of route, may be "bip" (Etherent) or "mstp" (Serial port). Use quotes.
+ device - Connection device, for example "eth0" or "/dev/ttyS0"; default values: for BIP:"eth0", for MSTP: "/dev/ttyS0". Use quotes.
+ network - Network number [1..65534]. Do not use network number 65535, it is broadcast number; default begins from 1 to routes count.
+
+bip arguments:
+ port - bip UDP port; default port is 47808 (0xBAC0).
+
+mstp arguments:
+ mac - MSTP MAC; default value is 127.
+ max_master - MSTP max master; default value is 127.
+ max_frames - 1. Segmentation does not supported.
+ baud - one from the list: 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 9600, 19200, 38400, 57600, 115200, 230400; default baud is 9600
+ parity - one from the list (with quotes): "None", "Even", "Odd"; default parity "None". Use quotes.
+ databits - one from the list: 5, 6, 7, 8; default 8.
+ stopbits - 1 or 2; default 1.
+
+4.3. Example of configuration file.
+
+ ports =
+ (
+ {
+ device_type = "bip";
+ device = "eth0";
+ port = 47808;
+ network = 1;
+ },
+ {
+ device_type = "bip";
+ device = "eth1";
+ port = 47808;
+ network = 2;
+ },
+ {
+ device_type = "bip";
+ device = "eth1";
+ port = 47809;
+ network = 3;
+ },
+ {
+ device_type = "mstp";
+ device = "/dev/ttyS0";
+ mac = 1;
+ max_master = 127;
+ max_frames = 1;
+ baud = 38400;
+ parity = "None";
+ databits = 8;
+ stopbits = 1;
+ network = 4;
+ }
+ );
+
+
+-----------------------
+5. Start
+-----------------------
+
+1. Copy configuration file in the router executable directory
+2. Start the router with "router -c init.cfg" command in terminal
+
+
diff --git a/bacnet-stack/include/mstp.h b/bacnet-stack/include/mstp.h
index 85ea3dc7..f5520816 100644
--- a/bacnet-stack/include/mstp.h
+++ b/bacnet-stack/include/mstp.h
@@ -111,10 +111,10 @@ struct mstp_port_struct_t {
so that you can be atomic on 8 bit microcontrollers */
uint32_t(
*SilenceTimer) (
- void);
+ void * pArg);
void (
*SilenceTimerReset) (
- void);
+ void * pArg);
/* A timer used to measure and generate Reply Postponed frames. It is */
/* incremented by a timer process and is cleared by the Master Node State */
@@ -163,6 +163,10 @@ struct mstp_port_struct_t {
hold contiguous memory. */
uint8_t *OutputBuffer;
uint16_t OutputBufferSize;
+
+ /*Platform-specific port data*/
+ void * UserData;
+
};
#ifdef __cplusplus
diff --git a/bacnet-stack/ports/linux/bip-init.c b/bacnet-stack/ports/linux/bip-init.c
index d9a5588c..508d6703 100644
--- a/bacnet-stack/ports/linux/bip-init.c
+++ b/bacnet-stack/ports/linux/bip-init.c
@@ -78,7 +78,7 @@ static int get_local_ifr_ioctl(
return rv;
}
-static int get_local_address_ioctl(
+int get_local_address_ioctl(
char *ifname,
struct in_addr *addr,
int request)
diff --git a/bacnet-stack/ports/linux/dlmstp.c b/bacnet-stack/ports/linux/dlmstp.c
index 475db2a3..fc191087 100644
--- a/bacnet-stack/ports/linux/dlmstp.c
+++ b/bacnet-stack/ports/linux/dlmstp.c
@@ -100,7 +100,7 @@ static uint8_t Tusage_timeout = 60;
static struct timeval start;
static uint32_t Timer_Silence(
- void)
+ void * pArg)
{
struct timeval now, tmp_diff;
int32_t res;
@@ -113,7 +113,7 @@ static uint32_t Timer_Silence(
}
static void Timer_Silence_Reset(
- void)
+ void * pArg)
{
gettimeofday(&start, NULL);
}
@@ -221,7 +221,7 @@ static void *dlmstp_master_fsm_task(
if (MSTP_Port.ReceivedValidFrame || MSTP_Port.ReceivedInvalidFrame) {
run_master = true;
} else {
- silence = MSTP_Port.SilenceTimer();
+ silence = MSTP_Port.SilenceTimer(NULL);
switch (MSTP_Port.master_state) {
case MSTP_MASTER_STATE_IDLE:
if (silence >= Tno_token)
diff --git a/bacnet-stack/ports/linux/dlmstp_linux.c b/bacnet-stack/ports/linux/dlmstp_linux.c
new file mode 100644
index 00000000..a79e26a0
--- /dev/null
+++ b/bacnet-stack/ports/linux/dlmstp_linux.c
@@ -0,0 +1,978 @@
+/**************************************************************************
+*
+* Copyright (C) 2008 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 "bacdef.h"
+#include "bacaddr.h"
+#include "mstp.h"
+//#include "dlmstp.h"
+#include "dlmstp_linux.h"
+#include "rs485.h"
+#include "npdu.h"
+#include "bits.h"
+/* OS Specific include */
+#include "net.h"
+#include "ringbuf.h"
+
+/** @file linux/dlmstp.c Provides Linux-specific DataLink functions for MS/TP. */
+
+#define BACNET_PDU_CONTROL_BYTE_OFFSET 1
+#define BACNET_DATA_EXPECTING_REPLY_BIT 2
+#define BACNET_DATA_EXPECTING_REPLY(control) ( (control & (1 << BACNET_DATA_EXPECTING_REPLY_BIT) ) > 0 )
+
+#define INCREMENT_AND_LIMIT_UINT16(x) {if (x < 0xFFFF) x++;}
+uint32_t Timer_Silence(
+ void * poPort)
+{
+ struct timeval now, tmp_diff;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return -1;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return -1;
+ }
+
+ int32_t res;
+
+ gettimeofday(&now, NULL);
+ timersub(&poSharedData->start, &now, &tmp_diff);
+ res = ((tmp_diff.tv_sec) * 1000 + (tmp_diff.tv_usec) / 1000);
+
+ return (res >= 0 ? res : -res);
+}
+
+void Timer_Silence_Reset(
+ void * poPort)
+{
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+
+ gettimeofday(&poSharedData->start, NULL);
+}
+
+void get_abstime(
+ struct timespec *abstime,
+ unsigned long milliseconds)
+{
+ struct timeval now, offset, result;
+
+ gettimeofday(&now, NULL);
+ offset.tv_sec = 0;
+ offset.tv_usec = milliseconds * 1000;
+ timeradd(&now, &offset, &result);
+ abstime->tv_sec = result.tv_sec;
+ abstime->tv_nsec = result.tv_usec * 1000;
+}
+
+void dlmstp_cleanup(
+ void * poPort)
+{
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+
+ /* restore the old port settings */
+ tcsetattr(poSharedData->RS485_Handle, TCSANOW, &poSharedData->RS485_oldtio);
+ close(poSharedData->RS485_Handle);
+
+ pthread_cond_destroy(&poSharedData->Received_Frame_Flag);
+ pthread_cond_destroy(&poSharedData->Receive_Packet_Flag);
+ pthread_cond_destroy(&poSharedData->Master_Done_Flag);
+ pthread_mutex_destroy(&poSharedData->Received_Frame_Mutex);
+ pthread_mutex_destroy(&poSharedData->Receive_Packet_Mutex);
+ pthread_mutex_destroy(&poSharedData->Master_Done_Mutex);
+}
+
+/* returns number of bytes sent on success, zero on failure */
+int dlmstp_send_pdu(
+ void * poPort,
+ 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 bytes_sent = 0;
+ struct mstp_pdu_packet *pkt;
+ unsigned i = 0;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return 0;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+
+ pkt = (struct mstp_pdu_packet *) Ringbuf_Alloc(&poSharedData->PDU_Queue);
+ if (pkt) {
+ pkt->data_expecting_reply = BACNET_DATA_EXPECTING_REPLY(pdu[BACNET_PDU_CONTROL_BYTE_OFFSET]);
+ for (i = 0; i < pdu_len; i++) {
+ pkt->buffer[i] = pdu[i];
+ }
+ pkt->length = pdu_len;
+ pkt->destination_mac = dest->mac[0];
+ bytes_sent = pdu_len;
+ }
+
+ return bytes_sent;
+}
+
+uint16_t dlmstp_receive(
+ void * poPort,
+ 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;
+ struct timespec abstime;
+ int rv = 0;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return 0;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+ (void) max_pdu;
+ /* see if there is a packet available, and a place
+ to put the reply (if necessary) and process it */
+ get_abstime(&abstime, timeout);
+ rv = pthread_cond_timedwait(&poSharedData->Receive_Packet_Flag, &poSharedData->Receive_Packet_Mutex,
+ &abstime);
+ if (rv == 0) {
+ if (poSharedData->Receive_Packet.ready) {
+ if (poSharedData->Receive_Packet.pdu_len) {
+ poSharedData->MSTP_Packets++;
+ if (src) {
+ memmove(src, &poSharedData->Receive_Packet.address,
+ sizeof(poSharedData->Receive_Packet.address));
+ }
+ if (pdu) {
+ memmove(pdu, &poSharedData->Receive_Packet.pdu,
+ sizeof(poSharedData->Receive_Packet.pdu));
+ }
+ pdu_len = poSharedData->Receive_Packet.pdu_len;
+ }
+ poSharedData->Receive_Packet.ready = false;
+ }
+ }
+
+ return pdu_len;
+}
+
+void *dlmstp_receive_fsm_task(
+ void *pArg)
+{
+ bool received_frame;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t*)pArg;
+ if(!mstp_port)
+ {
+ return NULL;
+ }
+
+ poSharedData = (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t*)pArg)->UserData;
+ if(!poSharedData)
+ {
+ return NULL;
+ }
+
+ for (;;) {
+ /* only do receive state machine while we don't have a frame */
+ if ((mstp_port->ReceivedValidFrame == false) &&
+ (mstp_port->ReceivedInvalidFrame == false)) {
+ do {
+ RS485_Check_UART_Data(mstp_port);
+ MSTP_Receive_Frame_FSM((volatile struct mstp_port_struct_t*)pArg);
+ received_frame = mstp_port->ReceivedValidFrame ||
+ mstp_port->ReceivedInvalidFrame;
+ if (received_frame) {
+ pthread_cond_signal(&poSharedData->Received_Frame_Flag);
+ break;
+ }
+ } while (mstp_port->DataAvailable);
+ }
+ }
+
+ return NULL;
+}
+
+void *dlmstp_master_fsm_task(
+ void *pArg)
+{
+ uint32_t silence = 0;
+ bool run_master = false;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t*)pArg;
+ if(!mstp_port)
+ {
+ return NULL;
+ }
+
+ poSharedData = (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t*)pArg)->UserData;
+ if(!poSharedData)
+ {
+ return NULL;
+ }
+
+ for (;;) {
+ if (mstp_port->ReceivedValidFrame == false &&
+ mstp_port->ReceivedInvalidFrame == false) {
+ RS485_Check_UART_Data(mstp_port);
+ MSTP_Receive_Frame_FSM(mstp_port);
+ }
+ if (mstp_port->ReceivedValidFrame || mstp_port->ReceivedInvalidFrame) {
+ run_master = true;
+ } else {
+ silence = mstp_port->SilenceTimer(NULL);
+ switch (mstp_port->master_state) {
+ case MSTP_MASTER_STATE_IDLE:
+ if (silence >= Tno_token)
+ run_master = true;
+ break;
+ case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
+ if (silence >= poSharedData->Treply_timeout)
+ run_master = true;
+ break;
+ case MSTP_MASTER_STATE_POLL_FOR_MASTER:
+ if (silence >= poSharedData->Tusage_timeout)
+ run_master = true;
+ break;
+ default:
+ run_master = true;
+ break;
+ }
+ }
+ if (run_master) {
+ if (mstp_port->This_Station <= DEFAULT_MAX_MASTER) {
+ while (MSTP_Master_Node_FSM(mstp_port)) {
+ /* do nothing while immediate transitioning */
+ }
+ } else if (mstp_port->This_Station < 255) {
+ MSTP_Slave_Node_FSM(mstp_port);
+ }
+ }
+ }
+
+ return NULL;
+}
+
+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;
+ }
+}
+
+/* for the MS/TP state machine to use for putting received data */
+uint16_t MSTP_Put_Receive(
+ volatile struct mstp_port_struct_t *mstp_port)
+{
+ uint16_t pdu_len = 0;
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA*)mstp_port->UserData;
+
+ if(!poSharedData)
+ {
+ return 0;
+ }
+
+ if (!poSharedData->Receive_Packet.ready) {
+ /* bounds check - maybe this should send an abort? */
+ pdu_len = mstp_port->DataLength;
+ if (pdu_len > sizeof(poSharedData->Receive_Packet.pdu))
+ pdu_len = sizeof(poSharedData->Receive_Packet.pdu);
+ memmove((void *) &poSharedData->Receive_Packet.pdu[0],
+ (void *) &mstp_port->InputBuffer[0], pdu_len);
+ dlmstp_fill_bacnet_address(&poSharedData->Receive_Packet.address,
+ mstp_port->SourceAddress);
+ poSharedData->Receive_Packet.pdu_len = mstp_port->DataLength;
+ poSharedData->Receive_Packet.ready = true;
+ pthread_cond_signal(&poSharedData->Receive_Packet_Flag);
+ }
+
+ return pdu_len;
+}
+
+/* for the MS/TP state machine to use for getting data to send */
+/* Return: amount of PDU data */
+uint16_t MSTP_Get_Send(
+ volatile struct mstp_port_struct_t * mstp_port,
+ unsigned timeout)
+{ /* milliseconds to wait for a packet */
+ uint16_t pdu_len = 0;
+ uint8_t frame_type = 0;
+ struct mstp_pdu_packet *pkt;
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA*)mstp_port->UserData;
+
+ if(!poSharedData)
+ {
+ return 0;
+ }
+
+ (void) timeout;
+ if (Ringbuf_Empty(&poSharedData->PDU_Queue)) {
+ return 0;
+ }
+ pkt = (struct mstp_pdu_packet *) Ringbuf_Pop_Front(&poSharedData->PDU_Queue);
+ if (pkt->data_expecting_reply) {
+ frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
+ } else {
+ frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
+ }
+ /* convert the PDU into the MSTP Frame */
+ pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */
+ mstp_port->OutputBufferSize, frame_type, pkt->destination_mac,
+ mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length);
+
+ return pdu_len;
+}
+
+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;
+
+ /* unused parameters */
+ request_pdu_len = request_pdu_len;
+ reply_pdu_len = reply_pdu_len;
+ /* 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) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Request is Network message.\n");
+#endif
+ return false;
+ }
+ request.pdu_type = request_pdu[offset] & 0xF0;
+ if (request.pdu_type != PDU_TYPE_CONFIRMED_SERVICE_REQUEST) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Not Confirmed Request.\n");
+#endif
+ 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) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Reply is Network message.\n");
+#endif
+ return false;
+ }
+ /* reply could be a lot of things:
+ confirmed, simple ack, abort, reject, error */
+ reply.pdu_type = reply_pdu[offset] & 0xF0;
+ switch (reply.pdu_type) {
+ case PDU_TYPE_CONFIRMED_SERVICE_REQUEST:
+ reply.invoke_id = reply_pdu[offset + 2];
+ /* segmented message? */
+ if (reply_pdu[offset] & BIT3) {
+ reply.service_choice = reply_pdu[offset + 5];
+ } else {
+ reply.service_choice = reply_pdu[offset + 3];
+ }
+ break;
+ case PDU_TYPE_SIMPLE_ACK:
+ reply.invoke_id = reply_pdu[offset + 1];
+ reply.service_choice = reply_pdu[offset + 2];
+ break;
+ case PDU_TYPE_COMPLEX_ACK:
+ reply.invoke_id = reply_pdu[offset + 1];
+ /* segmented message? */
+ if (reply_pdu[offset] & BIT3) {
+ reply.service_choice = reply_pdu[offset + 4];
+ } else {
+ reply.service_choice = reply_pdu[offset + 2];
+ }
+ break;
+ case PDU_TYPE_ERROR:
+ reply.invoke_id = reply_pdu[offset + 1];
+ reply.service_choice = reply_pdu[offset + 2];
+ break;
+ case PDU_TYPE_REJECT:
+ case PDU_TYPE_ABORT:
+ reply.invoke_id = reply_pdu[offset + 1];
+ break;
+ default:
+ return false;
+ }
+ /* these don't have service choice included */
+ if ((reply.pdu_type == PDU_TYPE_REJECT) ||
+ (reply.pdu_type == PDU_TYPE_ABORT)) {
+ if (request.invoke_id != reply.invoke_id) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n");
+#endif
+ return false;
+ }
+ } else {
+ if (request.invoke_id != reply.invoke_id) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Invoke ID mismatch.\n");
+#endif
+ return false;
+ }
+ if (request.service_choice != reply.service_choice) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "Service choice mismatch.\n");
+#endif
+ return false;
+ }
+ }
+ if (request.npdu_data.protocol_version != reply.npdu_data.protocol_version) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: "
+ "NPDU Protocol Version mismatch.\n");
+#endif
+ return false;
+ }
+ if (request.npdu_data.priority != reply.npdu_data.priority) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "NPDU Priority mismatch.\n");
+#endif
+ return false;
+ }
+ if (!bacnet_address_same(&request.address, &reply.address)) {
+#if PRINT_ENABLED
+ fprintf(stderr,
+ "DLMSTP: DER Compare failed: " "BACnet Address mismatch.\n");
+#endif
+ return false;
+ }
+
+ return true;
+}
+
+/* Get the reply to a DATA_EXPECTING_REPLY frame, or nothing */
+uint16_t MSTP_Get_Reply(
+ volatile struct mstp_port_struct_t * mstp_port,
+ unsigned timeout)
+{ /* milliseconds to wait for a packet */
+ uint16_t pdu_len = 0; /* return value */
+ bool matched = false;
+ uint8_t frame_type = 0;
+ struct mstp_pdu_packet *pkt;
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA*)mstp_port->UserData;
+
+ if(!poSharedData)
+ {
+ return 0;
+ }
+
+ if (Ringbuf_Empty(&poSharedData->PDU_Queue)) {
+ return 0;
+ }
+ pkt = (struct mstp_pdu_packet *) Ringbuf_Get_Front(&poSharedData->PDU_Queue);
+ /* is this the reply to the DER? */
+ matched =
+ dlmstp_compare_data_expecting_reply(&mstp_port->InputBuffer[0],
+ mstp_port->DataLength, mstp_port->SourceAddress,
+ (uint8_t *) & pkt->buffer[0], pkt->length, pkt->destination_mac);
+ if (!matched) {
+ return 0;
+ }
+ pkt = (struct mstp_pdu_packet *) Ringbuf_Pop_Front(&poSharedData->PDU_Queue);
+ if (pkt->data_expecting_reply) {
+ frame_type = FRAME_TYPE_BACNET_DATA_EXPECTING_REPLY;
+ } else {
+ frame_type = FRAME_TYPE_BACNET_DATA_NOT_EXPECTING_REPLY;
+ }
+ /* convert the PDU into the MSTP Frame */
+ pdu_len = MSTP_Create_Frame(&mstp_port->OutputBuffer[0], /* <-- loading this */
+ mstp_port->OutputBufferSize, frame_type, pkt->destination_mac,
+ mstp_port->This_Station, (uint8_t *) & pkt->buffer[0], pkt->length);
+
+ return pdu_len;
+}
+
+void dlmstp_set_mac_address(
+ void * poPort,
+ uint8_t mac_address)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+/*
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+*/
+ /* Master Nodes can only have address 0-127 */
+ if (mac_address <= 127) {
+ mstp_port->This_Station = mac_address;
+ /* FIXME: implement your data storage */
+ /* I2C_Write_Byte(
+ EEPROM_DEVICE_ADDRESS,
+ mac_address,
+ EEPROM_MSTP_MAC_ADDR); */
+ if (mac_address > mstp_port->Nmax_master)
+ dlmstp_set_max_master(mstp_port, mac_address);
+ }
+
+ return;
+}
+
+uint8_t dlmstp_mac_address(
+ void * poPort)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return 0;
+ }
+/* poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+*/
+
+ return mstp_port->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(
+ void * poPort,
+ uint8_t max_info_frames)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+/*
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+*/
+ if (max_info_frames >= 1) {
+ mstp_port->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 * poPort)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return 0;
+ }
+/*
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+*/
+ return mstp_port->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(
+ void * poPort,
+ uint8_t max_master)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+/*
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+*/
+ if (max_master <= 127) {
+ if (mstp_port->This_Station <= max_master) {
+ mstp_port->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 * poPort)
+{
+// SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return 0;
+ }
+/*
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+*/
+ return mstp_port->Nmax_master;
+}
+
+/* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */
+void dlmstp_set_baud_rate(
+ void * poPort,
+ uint32_t baud)
+{
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+
+ switch (baud) {
+ case 9600:
+ poSharedData->RS485_Baud = B9600;
+ break;
+ case 19200:
+ poSharedData->RS485_Baud = B19200;
+ break;
+ case 38400:
+ poSharedData->RS485_Baud = B38400;
+ break;
+ case 57600:
+ poSharedData->RS485_Baud = B57600;
+ break;
+ case 115200:
+ poSharedData->RS485_Baud = B115200;
+ break;
+ default:
+ break;
+ }
+}
+
+uint32_t dlmstp_baud_rate(
+ void * poPort)
+{
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return false;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return false;
+ }
+
+ switch (poSharedData->RS485_Baud) {
+ case B19200:
+ return 19200;
+ case B38400:
+ return 38400;
+ case B57600:
+ return 57600;
+ case B115200:
+ return 115200;
+ default:
+ case B9600:
+ return 9600;
+ }
+}
+
+void dlmstp_get_my_address(
+ void * poPort,
+ BACNET_ADDRESS * my_address)
+{
+ int i = 0; /* counter */
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t *) poPort;
+ if(!mstp_port)
+ {
+ return;
+ }
+ poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return;
+ }
+ my_address->mac_len = 1;
+ my_address->mac[0] = mstp_port->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;
+}
+
+bool dlmstp_init(
+ void * poPort,
+ char *ifname)
+{
+ unsigned long hThread = 0;
+ int rv = 0;
+ SHARED_MSTP_DATA * poSharedData;
+ struct mstp_port_struct_t * mstp_port = (struct mstp_port_struct_t*)poPort;
+ if(!mstp_port)
+ {
+ return false;
+ }
+
+ poSharedData = (SHARED_MSTP_DATA *) ((struct mstp_port_struct_t*)mstp_port)->UserData;
+ if(!poSharedData)
+ {
+ return false;
+ }
+
+ poSharedData->RS485_Port_Name = ifname;
+ /* initialize PDU queue */
+ Ringbuf_Init(&poSharedData->PDU_Queue, (uint8_t *) & poSharedData->PDU_Buffer,
+ sizeof(struct mstp_pdu_packet), MSTP_PDU_PACKET_COUNT);
+ /* initialize packet queue */
+ poSharedData->Receive_Packet.ready = false;
+ poSharedData->Receive_Packet.pdu_len = 0;
+ rv = pthread_cond_init(&poSharedData->Receive_Packet_Flag, NULL);
+ if (rv != 0) {
+ fprintf(stderr,
+ "MS/TP Interface: %s\n cannot allocate PThread Condition.\n",
+ ifname);
+ exit(1);
+ }
+ rv = pthread_mutex_init(&poSharedData->Receive_Packet_Mutex, NULL);
+ if (rv != 0) {
+ fprintf(stderr,
+ "MS/TP Interface: %s\n cannot allocate PThread Mutex.\n", ifname);
+ exit(1);
+ }
+
+ struct termios newtio;
+ printf("RS485: Initializing %s", poSharedData->RS485_Port_Name);
+ /*
+ Open device for reading and writing.
+ Blocking mode - more CPU effecient
+ */
+ poSharedData->RS485_Handle = open(poSharedData->RS485_Port_Name, O_RDWR | O_NOCTTY | O_NONBLOCK/*| O_NDELAY */ );
+ if (poSharedData->RS485_Handle < 0) {
+ perror(poSharedData->RS485_Port_Name);
+ exit(-1);
+ }
+#if 0
+ /* non blocking for the read */
+ fcntl(poSharedData->RS485_Handle, F_SETFL, FNDELAY);
+#else
+ /* efficient blocking for the read */
+ fcntl(poSharedData->RS485_Handle, F_SETFL, 0);
+#endif
+ /* save current serial port settings */
+ tcgetattr(poSharedData->RS485_Handle, &poSharedData->RS485_oldtio);
+ /* clear struct for new port settings */
+ bzero(&newtio, sizeof(newtio));
+ /*
+ BAUDRATE: Set bps rate. You could also use cfsetispeed and cfsetospeed.
+ CRTSCTS : output hardware flow control (only used if the cable has
+ all necessary lines. See sect. 7 of Serial-HOWTO)
+ CLOCAL : local connection, no modem contol
+ CREAD : enable receiving characters
+ */
+ newtio.c_cflag = poSharedData->RS485_Baud | poSharedData->RS485MOD | CLOCAL | CREAD;
+ /* Raw input */
+ newtio.c_iflag = 0;
+ /* Raw output */
+ newtio.c_oflag = 0;
+ /* no processing */
+ newtio.c_lflag = 0;
+ /* activate the settings for the port after flushing I/O */
+ tcsetattr(poSharedData->RS485_Handle, TCSAFLUSH, &newtio);
+ /* flush any data waiting */
+ usleep(200000);
+ tcflush(poSharedData->RS485_Handle, TCIOFLUSH);
+ /* ringbuffer */
+ FIFO_Init(&poSharedData->Rx_FIFO, poSharedData->Rx_Buffer, sizeof(poSharedData->Rx_Buffer));
+ printf("=success!\n");
+ mstp_port->InputBuffer = &poSharedData->RxBuffer[0];
+ mstp_port->InputBufferSize = sizeof(poSharedData->RxBuffer);
+ mstp_port->OutputBuffer = &poSharedData->TxBuffer[0];
+ mstp_port->OutputBufferSize = sizeof(poSharedData->TxBuffer);
+ gettimeofday(&poSharedData->start, NULL);
+ mstp_port->SilenceTimer = Timer_Silence;
+ mstp_port->SilenceTimerReset = Timer_Silence_Reset;
+ MSTP_Init(mstp_port);
+#if PRINT_ENABLED
+ fprintf(stderr, "MS/TP MAC: %02X\n", mstp_port->This_Station);
+ fprintf(stderr, "MS/TP Max_Master: %02X\n", mstp_port->Nmax_master);
+ fprintf(stderr, "MS/TP Max_Info_Frames: %u\n", mstp_port->Nmax_info_frames);
+#endif
+
+ rv = pthread_create(&hThread, NULL, dlmstp_master_fsm_task, mstp_port);
+ if (rv != 0) {
+ fprintf(stderr, "Failed to start Master Node FSM task\n");
+ }
+
+ return true;
+}
diff --git a/bacnet-stack/ports/linux/dlmstp_linux.h b/bacnet-stack/ports/linux/dlmstp_linux.h
new file mode 100644
index 00000000..4b6f2682
--- /dev/null
+++ b/bacnet-stack/ports/linux/dlmstp_linux.h
@@ -0,0 +1,210 @@
+/**************************************************************************
+*
+* Copyright (C) 2012 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 DLMSTP_LINUX_H
+#define DLMSTP_LINUX_H
+
+#include "mstp.h"
+//#include "dlmstp.h"
+#include "bits/pthreadtypes.h"
+
+#include
+#include
+#include
+#include "bacdef.h"
+#include "npdu.h"
+#include
+#include "fifo.h"
+#include "ringbuf.h"
+/* defines specific to MS/TP */
+/* preamble+type+dest+src+len+crc8+crc16 */
+#define MAX_HEADER (2+1+1+1+2+1+2)
+#define MAX_MPDU (MAX_HEADER+MAX_PDU)
+
+/* count must be a power of 2 for ringbuf library */
+#ifndef MSTP_PDU_PACKET_COUNT
+#define MSTP_PDU_PACKET_COUNT 8
+#endif
+
+typedef struct dlmstp_packet {
+ bool ready; /* true if ready to be sent or received */
+ BACNET_ADDRESS address; /* source address */
+ uint8_t frame_type; /* type of message */
+ uint16_t pdu_len; /* packet length */
+ uint8_t pdu[MAX_MPDU]; /* packet */
+} DLMSTP_PACKET;
+
+/* data structure for MS/TP PDU Queue */
+struct mstp_pdu_packet {
+ bool data_expecting_reply;
+ uint8_t destination_mac;
+ uint16_t length;
+ uint8_t buffer[MAX_MPDU];
+};
+
+typedef struct shared_mstp_data
+{
+ /* Number of MS/TP Packets Rx/Tx */
+ uint16_t MSTP_Packets;
+
+ /* packet queues */
+ DLMSTP_PACKET Receive_Packet;
+ DLMSTP_PACKET Transmit_Packet;
+ /*
+ RT_COND Receive_Packet_Flag;
+ RT_MUTEX Receive_Packet_Mutex;
+ */
+ pthread_cond_t Receive_Packet_Flag;
+ pthread_mutex_t Receive_Packet_Mutex;
+ /* mechanism to wait for a frame in state machine */
+ /*
+ RT_COND Received_Frame_Flag;
+ RT_MUTEX Received_Frame_Mutex;
+ */
+ pthread_cond_t Received_Frame_Flag;
+ pthread_mutex_t Received_Frame_Mutex;
+ pthread_cond_t Master_Done_Flag;
+ pthread_mutex_t Master_Done_Mutex;
+ /* buffers needed by mstp port struct */
+ uint8_t TxBuffer[MAX_MPDU];
+ uint8_t RxBuffer[MAX_MPDU];
+ /* 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.) */
+ uint16_t Treply_timeout;
+ /* 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.) */
+ uint8_t Tusage_timeout;
+ /* Timer that indicates line silence - and functions */
+ uint16_t SilenceTime;
+
+ /* handle returned from open() */
+ int RS485_Handle;
+ /* baudrate settings are defined in , which is
+ included by */
+ unsigned int RS485_Baud;
+ /* serial port name, /dev/ttyS0,
+ /dev/ttyUSB0 for USB->RS485 from B&B Electronics USOPTL4 */
+ char *RS485_Port_Name;
+ /* serial I/O settings */
+ struct termios RS485_oldtio;
+ /* some terminal I/O have RS-485 specific functionality */
+ tcflag_t RS485MOD;
+ /* Ring buffer for incoming bytes, in order to speed up the receiving. */
+ FIFO_BUFFER Rx_FIFO;
+ /* buffer size needs to be a power of 2 */
+ uint8_t Rx_Buffer[4096];
+ struct timeval start;
+
+ RING_BUFFER PDU_Queue;
+
+ struct mstp_pdu_packet PDU_Buffer[MSTP_PDU_PACKET_COUNT];
+
+}SHARED_MSTP_DATA;
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+ bool dlmstp_init(
+ void * poShared,
+ char *ifname);
+ void dlmstp_reset(
+ void * poShared);
+ void dlmstp_cleanup(
+ void * poShared);
+
+ /* returns number of bytes sent on success, negative on failure */
+ int dlmstp_send_pdu(
+ void * poShared,
+ BACNET_ADDRESS * dest, /* destination address */
+ uint8_t * pdu, /* any data to be sent - may be null */
+ unsigned pdu_len); /* number of bytes of data */
+
+ /* returns the number of octets in the PDU, or zero on failure */
+ uint16_t dlmstp_receive(
+ void * poShared,
+ 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 */
+
+ /* 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(
+ void * poShared,
+ uint8_t max_info_frames);
+ uint8_t dlmstp_max_info_frames(
+ void * poShared);
+
+ /* 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(
+ void * poShared,
+ uint8_t max_master);
+ uint8_t dlmstp_max_master(
+ void * poShared);
+
+ /* MAC address 0-127 */
+ void dlmstp_set_mac_address(
+ void * poShared,
+ uint8_t my_address);
+ uint8_t dlmstp_mac_address(
+ void * poShared);
+
+ void dlmstp_get_my_address(
+ void * poShared,
+ BACNET_ADDRESS * my_address);
+ void dlmstp_get_broadcast_address(
+ BACNET_ADDRESS * dest); /* destination address */
+
+ /* RS485 Baud Rate 9600, 19200, 38400, 57600, 115200 */
+ void dlmstp_set_baud_rate(
+ void * poShared,
+ uint32_t baud);
+ uint32_t dlmstp_baud_rate(
+ void * poShared);
+
+ void dlmstp_fill_bacnet_address(
+ BACNET_ADDRESS * src,
+ uint8_t mstp_address);
+
+ bool dlmstp_sole_master(
+ void);
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /*DLMSTP_LINUX_H*/
diff --git a/bacnet-stack/ports/linux/rs485.c b/bacnet-stack/ports/linux/rs485.c
index 6a78434b..0331f3d1 100644
--- a/bacnet-stack/ports/linux/rs485.c
+++ b/bacnet-stack/ports/linux/rs485.c
@@ -63,6 +63,8 @@
#include
#include
+#include "dlmstp_linux.h"
+
/* Posix serial programming reference:
http://www.easysw.com/~mike/serial/serial.html */
@@ -195,6 +197,87 @@ uint32_t RS485_Get_Baud_Rate(
return baud;
}
+/****************************************************************************
+* DESCRIPTION: Returns the baud rate that we are currently running at
+* RETURN: none
+* ALGORITHM: none
+* NOTES: none
+*****************************************************************************/
+uint32_t RS485_Get_Port_Baud_Rate(
+ volatile struct mstp_port_struct_t *mstp_port)
+{
+ uint32_t baud = 0;
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ return 0;
+ }
+ switch (poSharedData->RS485_Baud) {
+ case B0:
+ baud = 0;
+ break;
+ case B50:
+ baud = 50;
+ break;
+ case B75:
+ baud = 75;
+ break;
+ case B110:
+ baud = 110;
+ break;
+ case B134:
+ baud = 134;
+ break;
+ case B150:
+ baud = 150;
+ break;
+ case B200:
+ baud = 200;
+ break;
+ case B300:
+ baud = 300;
+ break;
+ case B600:
+ baud = 600;
+ break;
+ case B1200:
+ baud = 1200;
+ break;
+ case B1800:
+ baud = 1800;
+ break;
+ case B2400:
+ baud = 2400;
+ break;
+ case B4800:
+ baud = 4800;
+ break;
+ case B9600:
+ baud = 9600;
+ break;
+ case B19200:
+ baud = 19200;
+ break;
+ case B38400:
+ baud = 38400;
+ break;
+ case B57600:
+ baud = 57600;
+ break;
+ case B115200:
+ baud = 115200;
+ break;
+ case B230400:
+ baud = 230400;
+ break;
+ default:
+ baud = 9600;
+ break;
+ }
+
+ return baud;
+}
+
/****************************************************************************
* DESCRIPTION: Sets the baud rate for the chip USART
* RETURN: none
@@ -286,35 +369,66 @@ void RS485_Send_Frame(
volatile struct mstp_port_struct_t *mstp_port, /* port specific data */
uint8_t * buffer, /* frame to send (up to 501 bytes of data) */
uint16_t nbytes)
-{ /* number of bytes of data (up to 501) */
+{ /* number of bytes of data (up to 501) */
uint32_t turnaround_time = Tturnaround * 1000;
- uint32_t baud = RS485_Get_Baud_Rate();
+ uint32_t baud;
ssize_t written = 0;
int greska;
-
- /* sleeping for turnaround time is necessary to give other devices
- time to change from sending to receiving state. */
- usleep(turnaround_time / baud);
- /*
- On success, the number of bytes written are returned (zero indicates
- nothing was written). On error, -1 is returned, and errno is set
- appropriately. If count is zero and the file descriptor refers to a
- regular file, 0 will be returned without causing any other effect. For
- a special file, the results are not portable.
- */
- written = write(RS485_Handle, buffer, nbytes);
- greska = errno;
- if (written <= 0) {
- printf("write error: %s\n", strerror(greska));
- } else {
- /* wait until all output has been transmitted. */
- tcdrain(RS485_Handle);
- }
- /* tcdrain(RS485_Handle); */
- /* per MSTP spec, sort of */
- if (mstp_port) {
- mstp_port->SilenceTimerReset();
- }
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ baud = RS485_Get_Baud_Rate();
+ /* sleeping for turnaround time is necessary to give other devices
+ time to change from sending to receiving state. */
+ usleep(turnaround_time / baud);
+ /*
+ On success, the number of bytes written are returned (zero indicates
+ nothing was written). On error, -1 is returned, and errno is set
+ appropriately. If count is zero and the file descriptor refers to a
+ regular file, 0 will be returned without causing any other effect. For
+ a special file, the results are not portable.
+ */
+ written = write(RS485_Handle, buffer, nbytes);
+ greska = errno;
+ if (written <= 0) {
+ printf("write error: %s\n", strerror(greska));
+ } else {
+ /* wait until all output has been transmitted. */
+ tcdrain(RS485_Handle);
+ }
+ /* tcdrain(RS485_Handle); */
+ /* per MSTP spec, sort of */
+ if (mstp_port) {
+ mstp_port->SilenceTimerReset((void*)mstp_port);
+ }
+ }
+ else
+ {
+ baud = RS485_Get_Port_Baud_Rate(mstp_port);
+ /* sleeping for turnaround time is necessary to give other devices
+ time to change from sending to receiving state. */
+ usleep(turnaround_time / baud);
+ /*
+ On success, the number of bytes written are returned (zero indicates
+ nothing was written). On error, -1 is returned, and errno is set
+ appropriately. If count is zero and the file descriptor refers to a
+ regular file, 0 will be returned without causing any other effect. For
+ a special file, the results are not portable.
+ */
+ written = write(poSharedData->RS485_Handle, buffer, nbytes);
+ greska = errno;
+ if (written <= 0) {
+ printf("write error: %s\n", strerror(greska));
+ } else {
+ /* wait until all output has been transmitted. */
+ tcdrain(poSharedData->RS485_Handle);
+ }
+ /* tcdrain(RS485_Handle); */
+ /* per MSTP spec, sort of */
+ if (mstp_port) {
+ mstp_port->SilenceTimerReset((void*)mstp_port);
+ }
+ }
return;
}
@@ -333,37 +447,75 @@ void RS485_Check_UART_Data(
uint8_t buf[2048];
int n;
- if (mstp_port->ReceiveError == true) {
- /* do nothing but wait for state machine to clear the error */
- /* burning time, so wait a longer time */
- waiter.tv_sec = 0;
- waiter.tv_usec = 5000;
- } else if (mstp_port->DataAvailable == false) {
- /* wait for state machine to read from the DataRegister */
- if (FIFO_Count(&Rx_FIFO) > 0) {
- /* data is available */
- mstp_port->DataRegister = FIFO_Get(&Rx_FIFO);
- mstp_port->DataAvailable = true;
- /* FIFO is giving data - don't wait very long */
- waiter.tv_sec = 0;
- waiter.tv_usec = 10;
- } else {
- /* FIFO is empty - wait a longer time */
- waiter.tv_sec = 0;
- waiter.tv_usec = 5000;
- }
- }
- /* grab bytes and stuff them into the FIFO every time */
- FD_ZERO(&input);
- FD_SET(RS485_Handle, &input);
- n = select(RS485_Handle + 1, &input, NULL, NULL, &waiter);
- if (n < 0) {
- return;
- }
- if (FD_ISSET(RS485_Handle, &input)) {
- n = read(RS485_Handle, buf, sizeof(buf));
- FIFO_Add(&Rx_FIFO, &buf[0], n);
- }
+ SHARED_MSTP_DATA * poSharedData = (SHARED_MSTP_DATA *) mstp_port->UserData;
+ if(!poSharedData)
+ {
+ if (mstp_port->ReceiveError == true) {
+ /* do nothing but wait for state machine to clear the error */
+ /* burning time, so wait a longer time */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 5000;
+ } else if (mstp_port->DataAvailable == false) {
+ /* wait for state machine to read from the DataRegister */
+ if (FIFO_Count(&Rx_FIFO) > 0) {
+ /* data is available */
+ mstp_port->DataRegister = FIFO_Get(&Rx_FIFO);
+ mstp_port->DataAvailable = true;
+ /* FIFO is giving data - don't wait very long */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 10;
+ } else {
+ /* FIFO is empty - wait a longer time */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 5000;
+ }
+ }
+ /* grab bytes and stuff them into the FIFO every time */
+ FD_ZERO(&input);
+ FD_SET(RS485_Handle, &input);
+ n = select(RS485_Handle + 1, &input, NULL, NULL, &waiter);
+ if (n < 0) {
+ return;
+ }
+ if (FD_ISSET(RS485_Handle, &input)) {
+ n = read(RS485_Handle, buf, sizeof(buf));
+ FIFO_Add(&Rx_FIFO, &buf[0], n);
+ }
+ }
+ else
+ {
+ if (mstp_port->ReceiveError == true) {
+ /* do nothing but wait for state machine to clear the error */
+ /* burning time, so wait a longer time */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 5000;
+ } else if (mstp_port->DataAvailable == false) {
+ /* wait for state machine to read from the DataRegister */
+ if (FIFO_Count(&poSharedData->Rx_FIFO) > 0) {
+ /* data is available */
+ mstp_port->DataRegister = FIFO_Get(&poSharedData->Rx_FIFO);
+ mstp_port->DataAvailable = true;
+ /* FIFO is giving data - don't wait very long */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 10;
+ } else {
+ /* FIFO is empty - wait a longer time */
+ waiter.tv_sec = 0;
+ waiter.tv_usec = 5000;
+ }
+ }
+ /* grab bytes and stuff them into the FIFO every time */
+ FD_ZERO(&input);
+ FD_SET(poSharedData->RS485_Handle, &input);
+ n = select(poSharedData->RS485_Handle + 1, &input, NULL, NULL, &waiter);
+ if (n < 0) {
+ return;
+ }
+ if (FD_ISSET(poSharedData->RS485_Handle, &input)) {
+ n = read(poSharedData->RS485_Handle, buf, sizeof(buf));
+ FIFO_Add(&poSharedData->Rx_FIFO, &buf[0], n);
+ }
+ }
}
void RS485_Cleanup(
diff --git a/bacnet-stack/src/mstp.c b/bacnet-stack/src/mstp.c
index a26b6489..e8dc6a64 100644
--- a/bacnet-stack/src/mstp.c
+++ b/bacnet-stack/src/mstp.c
@@ -256,14 +256,14 @@ void MSTP_Receive_Frame_FSM(
mstptext_receive_state(mstp_port->receive_state),
mstp_port->DataRegister, mstp_port->HeaderCRC, mstp_port->Index,
mstp_port->EventCount, mstp_port->DataLength,
- mstp_port->SilenceTimer());
+ mstp_port->SilenceTimer((void*)mstp_port));
switch (mstp_port->receive_state) {
/* In the IDLE state, the node waits for the beginning of a frame. */
case MSTP_RECEIVE_STATE_IDLE:
/* EatAnError */
if (mstp_port->ReceiveError == true) {
mstp_port->ReceiveError = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* wait for the start of a frame. */
} else if (mstp_port->DataAvailable == true) {
@@ -279,14 +279,14 @@ void MSTP_Receive_Frame_FSM(
/* wait for the start of a frame. */
}
mstp_port->DataAvailable = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
}
break;
/* In the PREAMBLE state, the node waits for the second octet of the preamble. */
case MSTP_RECEIVE_STATE_PREAMBLE:
/* Timeout */
- if (mstp_port->SilenceTimer() > Tframe_abort) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) > Tframe_abort) {
/* a correct preamble has not been received */
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
@@ -294,7 +294,7 @@ void MSTP_Receive_Frame_FSM(
/* Error */
else if (mstp_port->ReceiveError == true) {
mstp_port->ReceiveError = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
@@ -317,25 +317,25 @@ void MSTP_Receive_Frame_FSM(
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
}
mstp_port->DataAvailable = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
}
break;
/* In the HEADER state, the node waits for the fixed message header. */
case MSTP_RECEIVE_STATE_HEADER:
/* Timeout */
- if (mstp_port->SilenceTimer() > Tframe_abort) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) > Tframe_abort) {
/* indicate that an error has occurred during the reception of a frame */
mstp_port->ReceivedInvalidFrame = true;
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
printf_receive_error("MSTP: Rx Header: SilenceTimer %u > %d\n",
- (unsigned) mstp_port->SilenceTimer(), Tframe_abort);
+ (unsigned) mstp_port->SilenceTimer((void*)mstp_port), Tframe_abort);
}
/* Error */
else if (mstp_port->ReceiveError == true) {
mstp_port->ReceiveError = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
/* indicate that an error has occurred during the reception of a frame */
mstp_port->ReceivedInvalidFrame = true;
@@ -460,7 +460,7 @@ void MSTP_Receive_Frame_FSM(
/* wait for the start of a frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
}
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
INCREMENT_AND_LIMIT_UINT8(mstp_port->EventCount);
mstp_port->DataAvailable = false;
}
@@ -469,19 +469,19 @@ void MSTP_Receive_Frame_FSM(
case MSTP_RECEIVE_STATE_DATA:
case MSTP_RECEIVE_STATE_SKIP_DATA:
/* Timeout */
- if (mstp_port->SilenceTimer() > Tframe_abort) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) > Tframe_abort) {
/* indicate that an error has occurred during the reception of a frame */
mstp_port->ReceivedInvalidFrame = true;
printf_receive_error
("MSTP: Rx Data: SilenceTimer %ums > %dms\n",
- (unsigned) mstp_port->SilenceTimer(), Tframe_abort);
+ (unsigned) mstp_port->SilenceTimer((void*)mstp_port), Tframe_abort);
/* wait for the start of the next frame. */
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
}
/* Error */
else if (mstp_port->ReceiveError == true) {
mstp_port->ReceiveError = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
/* indicate that an error has occurred during the reception of a frame */
mstp_port->ReceivedInvalidFrame = true;
printf_receive_error("MSTP: Rx Data: ReceiveError\n");
@@ -538,7 +538,7 @@ void MSTP_Receive_Frame_FSM(
mstp_port->receive_state = MSTP_RECEIVE_STATE_IDLE;
}
mstp_port->DataAvailable = false;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
}
break;
default:
@@ -582,7 +582,7 @@ bool MSTP_Master_Node_FSM(
mstp_port->This_Station, next_this_station,
mstp_port->Next_Station, next_next_station,
mstp_port->Poll_Station, next_poll_station, mstp_port->EventCount,
- mstp_port->TokenCount, mstp_port->SilenceTimer(),
+ mstp_port->TokenCount, mstp_port->SilenceTimer((void*)mstp_port),
mstptext_master_state(mstp_port->master_state));
}
switch (mstp_port->master_state) {
@@ -601,7 +601,7 @@ bool MSTP_Master_Node_FSM(
case MSTP_MASTER_STATE_IDLE:
/* In the IDLE state, the node waits for a frame. */
/* LostToken */
- if (mstp_port->SilenceTimer() >= Tno_token) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) >= Tno_token) {
/* assume that the token has been lost */
mstp_port->EventCount = 0; /* Addendum 135-2004d-8 */
mstp_port->master_state = MSTP_MASTER_STATE_NO_TOKEN;
@@ -620,7 +620,7 @@ bool MSTP_Master_Node_FSM(
"Src=%02X Dest=%02X DataLen=%u " "FC=%u ST=%u Type=%s\n",
mstp_port->SourceAddress, mstp_port->DestinationAddress,
mstp_port->DataLength, mstp_port->FrameCount,
- mstp_port->SilenceTimer(),
+ mstp_port->SilenceTimer((void*)mstp_port),
mstptext_frame_type((unsigned) mstp_port->FrameType));
/* destined for me! */
if ((mstp_port->DestinationAddress == mstp_port->This_Station)
@@ -731,7 +731,7 @@ bool MSTP_Master_Node_FSM(
case MSTP_MASTER_STATE_WAIT_FOR_REPLY:
/* In the WAIT_FOR_REPLY state, the node waits for */
/* a reply from another node. */
- if (mstp_port->SilenceTimer() >= Treply_timeout) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) >= Treply_timeout) {
/* ReplyTimeout */
/* assume that the request has failed */
mstp_port->FrameCount = mstp_port->Nmax_info_frames;
@@ -878,7 +878,7 @@ bool MSTP_Master_Node_FSM(
case MSTP_MASTER_STATE_PASS_TOKEN:
/* The PASS_TOKEN state listens for a successor to begin using */
/* the token that this node has just attempted to pass. */
- if (mstp_port->SilenceTimer() <= Tusage_timeout) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) <= Tusage_timeout) {
if (mstp_port->EventCount > Nmin_octets) {
/* SawTokenUser */
/* Assume that a frame has been sent by the new token user. */
@@ -922,7 +922,7 @@ bool MSTP_Master_Node_FSM(
/* for that period of time. The timeout is continued to determine */
/* whether or not this node may create a token. */
my_timeout = Tno_token + (Tslot * mstp_port->This_Station);
- if (mstp_port->SilenceTimer() < my_timeout) {
+ if (mstp_port->SilenceTimer((void*)mstp_port) < my_timeout) {
if (mstp_port->EventCount > Nmin_octets) {
/* SawFrame */
/* Some other node exists at a lower address. */
@@ -935,8 +935,8 @@ bool MSTP_Master_Node_FSM(
Tno_token + (Tslot * (mstp_port->This_Station + 1));
mm_timeout =
Tno_token + (Tslot * (mstp_port->Nmax_master + 1));
- if ((mstp_port->SilenceTimer() < ns_timeout) ||
- (mstp_port->SilenceTimer() > mm_timeout)) {
+ if ((mstp_port->SilenceTimer((void*)mstp_port) < ns_timeout) ||
+ (mstp_port->SilenceTimer((void*)mstp_port) > mm_timeout)) {
/* GenerateToken */
/* Assume that this node is the lowest numerical address */
/* on the network and is empowered to create a token. */
@@ -1000,7 +1000,7 @@ bool MSTP_Master_Node_FSM(
transition_now = true;
}
mstp_port->ReceivedValidFrame = false;
- } else if ((mstp_port->SilenceTimer() > Tusage_timeout) ||
+ } else if ((mstp_port->SilenceTimer((void*)mstp_port) > Tusage_timeout) ||
(mstp_port->ReceivedInvalidFrame == true)) {
if (mstp_port->SoleMaster == true) {
/* SoleMaster */
@@ -1069,7 +1069,7 @@ bool MSTP_Master_Node_FSM(
mstp_port->master_state = MSTP_MASTER_STATE_IDLE;
/* clear our flag we were holding for comparison */
mstp_port->ReceivedValidFrame = false;
- } else if (mstp_port->SilenceTimer() > Treply_delay) {
+ } else if (mstp_port->SilenceTimer((void*)mstp_port) > Treply_delay) {
/* DeferredReply */
/* If no reply will be available from the higher layers */
/* within Treply_delay after the reception of the */
@@ -1126,7 +1126,7 @@ void MSTP_Slave_Node_FSM(
(uint16_t) length);
/* clear our flag we were holding for comparison */
mstp_port->ReceivedValidFrame = false;
- } else if (mstp_port->SilenceTimer() > Treply_delay) {
+ } else if (mstp_port->SilenceTimer((void*)mstp_port) > Treply_delay) {
/* 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
@@ -1198,7 +1198,7 @@ void MSTP_Init(
mstp_port->ReceivedValidFrame = false;
mstp_port->ReceivedValidFrameNotForUs = false;
mstp_port->RetryCount = 0;
- mstp_port->SilenceTimerReset();
+ mstp_port->SilenceTimerReset((void*)mstp_port);
mstp_port->SoleMaster = false;
mstp_port->SourceAddress = 0;
mstp_port->TokenCount = 0;