Indented.
This commit is contained in:
@@ -1,380 +0,0 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "bacdef.h"
|
||||
#include "npdu.h"
|
||||
#include "arcnet.h"
|
||||
#include "net.h"
|
||||
|
||||
/* my local device data - MAC address */
|
||||
uint8_t ARCNET_MAC_Address = 0;
|
||||
/* ARCNET file handle */
|
||||
static int ARCNET_Sock_FD = -1;
|
||||
/* ARCNET socket address (has the interface name) */
|
||||
static struct sockaddr ARCNET_Socket_Address;
|
||||
/* Broadcast address */
|
||||
#define ARCNET_BROADCAST 0
|
||||
|
||||
/*
|
||||
Hints:
|
||||
|
||||
When using a PCI20-485D ARCNET card from Contemporary Controls,
|
||||
you might need to know about the following settings:
|
||||
|
||||
Assuming a 20MHz clock on the COM20020 chip:
|
||||
|
||||
clockp Clock Prescaler DataRate
|
||||
------ --------------- --------
|
||||
0 8 2.5 Mbps
|
||||
1 16 1.25 Mbps
|
||||
2 32 625 Kbps
|
||||
3 64 312.5 Kbps
|
||||
4 128 156.25Kbps
|
||||
|
||||
1. Install the arcnet driver and arcnet raw mode driver:
|
||||
# modprobe com20020_pci clockp=4
|
||||
# modprobe arc_rawmode
|
||||
|
||||
2. Use ifconfig to bring up the interface
|
||||
# ifconfig arc0 up
|
||||
|
||||
3. The hardware address (MAC address) is set using the dipswitch
|
||||
on the back of the card. 0 is broadcast, so don't use 0.
|
||||
|
||||
4. The backplane mode on the PCI20-485D card is done in hardware,
|
||||
so the driver does not need to do backplane mode. If you
|
||||
use another type of PCI20 card, you could pass in backplane=1 or
|
||||
backplane=0 as an option to the modprobe of com20020_pci.
|
||||
|
||||
*/
|
||||
|
||||
bool arcnet_valid(void)
|
||||
{
|
||||
return (ARCNET_Sock_FD >= 0);
|
||||
}
|
||||
|
||||
void arcnet_cleanup(void)
|
||||
{
|
||||
if (arcnet_valid())
|
||||
close(ARCNET_Sock_FD);
|
||||
ARCNET_Sock_FD = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int arcnet_bind(char *interface_name)
|
||||
{
|
||||
int sock_fd = -1; /* return value */
|
||||
struct ifreq ifr;
|
||||
int rv; /* return value - error value from df or ioctl call */
|
||||
int uid = 0;
|
||||
|
||||
/* check to see if we are being run as root */
|
||||
uid = getuid();
|
||||
if (uid != 0) {
|
||||
fprintf(stderr,
|
||||
"arcnet: Unable to open an af_packet socket. "
|
||||
"Try running with root priveleges.\n");
|
||||
return sock_fd;
|
||||
}
|
||||
fprintf(stderr, "arcnet: opening \"%s\"\n", interface_name);
|
||||
/* note: on some systems you may have to add or enable in */
|
||||
/* modules.conf (or in modutils/alias on Debian with update-modules) */
|
||||
/* alias net-pf-17 af_packet */
|
||||
/* Then follow it by: # modprobe af_packet */
|
||||
if ((sock_fd = socket(PF_PACKET, SOCK_PACKET, htons(ETH_P_ALL))) < 0) {
|
||||
/* Error occured */
|
||||
fprintf(stderr,
|
||||
"arcnet: Error opening socket: %s\n", strerror(errno));
|
||||
fprintf(stderr,
|
||||
"You might need to add the following to modules.conf\n"
|
||||
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
||||
"alias net-pf-17 af_packet\n"
|
||||
"Also, add af_packet to /etc/modules.\n"
|
||||
"Then follow it by:\n" "# modprobe af_packet\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (ARCNET_Sock_FD >= 0) {
|
||||
/* Bind the socket to an interface name so we only get packets from it */
|
||||
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
|
||||
/*ARCNET_Socket_Address.sa_family = PF_INET; */
|
||||
/* Clear the memory before copying */
|
||||
memset(ARCNET_Socket_Address.sa_data, '\0',
|
||||
sizeof(ARCNET_Socket_Address.sa_data));
|
||||
/* Strcpy the interface name into the address */
|
||||
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
|
||||
sizeof(ARCNET_Socket_Address.sa_data) - 1);
|
||||
fprintf(stderr, "arcnet: binding \"%s\"\n",
|
||||
ARCNET_Socket_Address.sa_data);
|
||||
if (bind(sock_fd, &ARCNET_Socket_Address,
|
||||
sizeof(ARCNET_Socket_Address)) != 0) {
|
||||
/* Bind problem, close socket and return */
|
||||
fprintf(stderr,
|
||||
"arcnet: Unable to bind socket : %s\n", strerror(errno));
|
||||
fprintf(stderr,
|
||||
"You might need to add the following to modules.conf\n"
|
||||
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
||||
"alias net-pf-17 af_packet\n"
|
||||
"Also, add af_packet to /etc/modules.\n"
|
||||
"Then follow it by:\n" "# modprobe af_packet\n");
|
||||
/* Close the socket */
|
||||
close(sock_fd);
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
strncpy(ifr.ifr_name, interface_name, sizeof(ifr.ifr_name));
|
||||
rv = ioctl(sock_fd, SIOCGIFHWADDR, &ifr);
|
||||
if (rv != -1) /* worked okay */
|
||||
ARCNET_MAC_Address = ifr.ifr_hwaddr.sa_data[0];
|
||||
/* copy this info into the local copy since bind wiped it out */
|
||||
ARCNET_Socket_Address.sa_family = ARPHRD_ARCNET;
|
||||
/*ARCNET_Socket_Address.sa_family = PF_INET; */
|
||||
/* Clear the memory before copying */
|
||||
memset(ARCNET_Socket_Address.sa_data, '\0',
|
||||
sizeof(ARCNET_Socket_Address.sa_data));
|
||||
/* Strcpy the interface name into the address */
|
||||
strncpy(ARCNET_Socket_Address.sa_data, interface_name,
|
||||
sizeof(ARCNET_Socket_Address.sa_data) - 1);
|
||||
fprintf(stderr, "arcnet: MAC=%02Xh iface=\"%s\"\n",
|
||||
ARCNET_MAC_Address, ARCNET_Socket_Address.sa_data);
|
||||
|
||||
atexit(arcnet_cleanup);
|
||||
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
bool arcnet_init(char *interface_name)
|
||||
{
|
||||
if (interface_name)
|
||||
ARCNET_Sock_FD = arcnet_bind(interface_name);
|
||||
else
|
||||
ARCNET_Sock_FD = arcnet_bind("arc0");
|
||||
|
||||
return arcnet_valid();
|
||||
}
|
||||
|
||||
/* function to send a PDU out the socket */
|
||||
/* returns number of bytes sent on success, negative on failure */
|
||||
int arcnet_send_pdu(BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len)
|
||||
{ /* number of bytes of data */
|
||||
BACNET_ADDRESS src = { 0 }; /* source address */
|
||||
int bytes = 0;
|
||||
uint8_t mtu[512] = { 0 };
|
||||
int mtu_len = 0;
|
||||
struct archdr *pkt = (struct archdr *) mtu;
|
||||
|
||||
(void) npdu_data;
|
||||
src.mac[0] = ARCNET_MAC_Address;
|
||||
src.mac_len = 1;
|
||||
|
||||
/* don't waste time if the socket is not valid */
|
||||
if (ARCNET_Sock_FD < 0) {
|
||||
fprintf(stderr, "arcnet: socket is invalid!\n");
|
||||
return -1;
|
||||
}
|
||||
/* load destination MAC address */
|
||||
if (dest->mac_len == 1)
|
||||
pkt->hard.dest = dest->mac[0];
|
||||
else {
|
||||
fprintf(stderr, "arcnet: invalid destination MAC address!\n");
|
||||
return -2;
|
||||
}
|
||||
if (src.mac_len == 1)
|
||||
pkt->hard.source = src.mac[0];
|
||||
else {
|
||||
fprintf(stderr, "arcnet: invalid source MAC address!\n");
|
||||
return -3;
|
||||
}
|
||||
/* Logical PDU portion */
|
||||
pkt->soft.raw[0] = 0xCD; /* SC for BACnet */
|
||||
pkt->soft.raw[1] = 0x82; /* DSAP for BACnet */
|
||||
pkt->soft.raw[2] = 0x82; /* SSAP for BACnet */
|
||||
pkt->soft.raw[3] = 0x03; /* LLC Control byte in header */
|
||||
/* packet length */
|
||||
mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC */ + pdu_len;
|
||||
if (mtu_len > 512) {
|
||||
fprintf(stderr, "arcnet: PDU is too big to send!\n");
|
||||
return -4;
|
||||
}
|
||||
memcpy(&pkt->soft.raw[4], pdu, pdu_len);
|
||||
/* Send the packet */
|
||||
bytes =
|
||||
sendto(ARCNET_Sock_FD, &mtu, mtu_len, 0,
|
||||
(struct sockaddr *) &ARCNET_Socket_Address,
|
||||
sizeof(ARCNET_Socket_Address));
|
||||
/* did it get sent? */
|
||||
if (bytes < 0)
|
||||
fprintf(stderr, "arcnet: Error sending packet: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* receives an framed packet */
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t arcnet_receive(BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout)
|
||||
{ /* milliseconds to wait for a packet */
|
||||
int received_bytes;
|
||||
uint8_t buf[512] = { 0 }; /* data */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
fd_set read_fds;
|
||||
int max;
|
||||
struct timeval select_timeout;
|
||||
struct archdr *pkt = (struct archdr *) buf;
|
||||
|
||||
/* Make sure the socket is open */
|
||||
if (ARCNET_Sock_FD <= 0)
|
||||
return 0;
|
||||
|
||||
/* we could just use a non-blocking socket, but that consumes all
|
||||
the CPU time. We can use a timeout; it is only supported as
|
||||
a select. */
|
||||
if (timeout >= 1000) {
|
||||
select_timeout.tv_sec = timeout / 1000;
|
||||
select_timeout.tv_usec =
|
||||
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||
} else {
|
||||
select_timeout.tv_sec = 0;
|
||||
select_timeout.tv_usec = 1000 * timeout;
|
||||
}
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(ARCNET_Sock_FD, &read_fds);
|
||||
max = ARCNET_Sock_FD;
|
||||
|
||||
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
|
||||
received_bytes = read(ARCNET_Sock_FD, &buf[0], sizeof(buf));
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* See if there is a problem */
|
||||
if (received_bytes < 0) {
|
||||
/* EAGAIN Non-blocking I/O has been selected */
|
||||
/* using O_NONBLOCK and no data */
|
||||
/* was immediately available for reading. */
|
||||
if (errno != EAGAIN)
|
||||
fprintf(stderr,
|
||||
"ethernet: Read error in receiving packet: %s\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (received_bytes == 0)
|
||||
return 0;
|
||||
|
||||
/* printf("arcnet: received %u bytes (offset=%02Xh %02Xh) "
|
||||
"from %02Xh (proto==%02Xh)\n",
|
||||
received_bytes, pkt->offset[0], pkt->offset[1],
|
||||
pkt->hard.source, pkt->soft.raw[0]);
|
||||
*/
|
||||
|
||||
if (pkt->hard.source == ARCNET_MAC_Address) {
|
||||
fprintf(stderr, "arcnet: self sent packet?\n");
|
||||
return 0;
|
||||
}
|
||||
if (pkt->soft.raw[0] != 0xCD) {
|
||||
/* fprintf(stderr,"arcnet: Non-BACnet packet.\n"); */
|
||||
return 0;
|
||||
}
|
||||
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
|
||||
(pkt->hard.dest != ARCNET_BROADCAST)) {
|
||||
fprintf(stderr, "arcnet: This packet is not for us.\n");
|
||||
return 0;
|
||||
}
|
||||
if ((pkt->soft.raw[1] != 0x82) || /* DSAP */
|
||||
(pkt->soft.raw[2] != 0x82) || /* LSAP */
|
||||
(pkt->soft.raw[3] != 0x03)) { /* LLC Control */
|
||||
fprintf(stderr, "arcnet: BACnet packet has invalid LLC.\n");
|
||||
return 0;
|
||||
}
|
||||
/* It must be addressed to us or be a Broadcast */
|
||||
if ((pkt->hard.dest != ARCNET_MAC_Address) &&
|
||||
(pkt->hard.dest != ARCNET_BROADCAST)) {
|
||||
fprintf(stderr, "arcnet: This packet is not for us.\n");
|
||||
return 0;
|
||||
}
|
||||
/* copy the source address */
|
||||
src->mac_len = 1;
|
||||
src->mac[0] = pkt->hard.source;
|
||||
/* compute the PDU length */
|
||||
pdu_len = received_bytes - ARC_HDR_SIZE;
|
||||
pdu_len -= 4 /* SC, DSAP, SSAP, LLC Control */ ;
|
||||
/* copy the buffer into the PDU */
|
||||
if (pdu_len < max_pdu)
|
||||
memmove(&pdu[0], &pkt->soft.raw[4], pdu_len);
|
||||
/* silently ignore packets that are too large */
|
||||
else
|
||||
pdu_len = 0;
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void arcnet_get_my_address(BACNET_ADDRESS * my_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
my_address->mac_len = 1;
|
||||
my_address->mac[0] = ARCNET_MAC_Address;
|
||||
my_address->net = 0; /* DNET=0 is local only, no routing */
|
||||
my_address->len = 0;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
my_address->adr[i] = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void arcnet_get_broadcast_address(BACNET_ADDRESS * dest)
|
||||
{ /* destination address */
|
||||
int i = 0; /* counter */
|
||||
|
||||
if (dest) {
|
||||
dest->mac[0] = ARCNET_BROADCAST;
|
||||
dest->mac_len = 1;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -1,148 +0,0 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
||||
#include <stdbool.h> /* for the standard bool type. */
|
||||
#include "bacdcode.h"
|
||||
#include "bip.h"
|
||||
#include "net.h"
|
||||
|
||||
|
||||
static int get_local_ifr_ioctl(char *ifname, struct ifreq *ifr,
|
||||
int request)
|
||||
{
|
||||
int fd;
|
||||
int rv; /* return value */
|
||||
|
||||
strncpy(ifr->ifr_name, ifname, sizeof(ifr->ifr_name));
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (fd < 0)
|
||||
rv = fd;
|
||||
else
|
||||
rv = ioctl(fd, request, ifr);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
static int get_local_address_ioctl(char *ifname,
|
||||
struct in_addr *addr, int request)
|
||||
{
|
||||
struct ifreq ifr = { {{0}} };
|
||||
struct sockaddr_in *tcpip_address;
|
||||
int rv; /* return value */
|
||||
|
||||
rv = get_local_ifr_ioctl(ifname, &ifr, request);
|
||||
if (rv >= 0) {
|
||||
tcpip_address = (struct sockaddr_in *) &ifr.ifr_addr;
|
||||
memcpy(addr, &tcpip_address->sin_addr, sizeof(struct in_addr));
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
/* on Linux, ifname is eth0, ath0, arc0, and others. */
|
||||
static void bip_set_interface(char *ifname)
|
||||
{
|
||||
struct in_addr local_address;
|
||||
struct in_addr broadcast_address;
|
||||
|
||||
/* setup local address */
|
||||
get_local_address_ioctl(ifname, &local_address, SIOCGIFADDR);
|
||||
bip_set_addr(local_address.s_addr);
|
||||
#ifdef BIP_DEBUG
|
||||
fprintf(stderr, "Interface: %s\n", ifname);
|
||||
fprintf(stderr, "IP Address: %s\n", inet_ntoa(local_address));
|
||||
#endif
|
||||
/* setup local broadcast address */
|
||||
get_local_address_ioctl(ifname, &broadcast_address, SIOCGIFBRDADDR);
|
||||
bip_set_broadcast_addr(broadcast_address.s_addr);
|
||||
#ifdef BIP_DEBUG
|
||||
fprintf(stderr, "IP Broadcast Address: %s\n",
|
||||
inet_ntoa(broadcast_address));
|
||||
fprintf(stderr, "UDP Port: 0x%04X [%hu]\n",
|
||||
bip_get_port(),
|
||||
bip_get_port());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool bip_init(char *ifname)
|
||||
{
|
||||
int status = 0; /* return from socket lib calls */
|
||||
struct sockaddr_in sin;
|
||||
int sockopt = 0;
|
||||
int sock_fd = -1;
|
||||
|
||||
if (ifname)
|
||||
bip_set_interface(ifname);
|
||||
else
|
||||
bip_set_interface("eth0");
|
||||
/* assumes that the driver has already been initialized */
|
||||
sock_fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
bip_set_socket(sock_fd);
|
||||
if (sock_fd < 0)
|
||||
return false;
|
||||
/* Allow us to use the same socket for sending and receiving */
|
||||
/* This makes sure that the src port is correct when sending */
|
||||
sockopt = 1;
|
||||
status = setsockopt(sock_fd, SOL_SOCKET, SO_REUSEADDR,
|
||||
&sockopt, sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
return status;
|
||||
}
|
||||
/* allow us to send a broadcast */
|
||||
status = setsockopt(sock_fd, SOL_SOCKET, SO_BROADCAST,
|
||||
&sockopt, sizeof(sockopt));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
return status;
|
||||
}
|
||||
/* bind the socket to the local port number and IP address */
|
||||
sin.sin_family = AF_INET;
|
||||
sin.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sin.sin_port = htons(bip_get_port());
|
||||
memset(&(sin.sin_zero), '\0', sizeof(sin.sin_zero));
|
||||
status = bind(sock_fd,
|
||||
(const struct sockaddr *) &sin, sizeof(struct sockaddr));
|
||||
if (status < 0) {
|
||||
close(sock_fd);
|
||||
bip_set_socket(-1);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,409 +0,0 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2005 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307, USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
#include <stdint.h> /* for standard integer types uint8_t etc. */
|
||||
#include <stdbool.h> /* for the standard bool type. */
|
||||
|
||||
#include "net.h"
|
||||
#include "bacdef.h"
|
||||
#include "ethernet.h"
|
||||
#include "bacdcode.h"
|
||||
|
||||
/* commonly used comparison address for ethernet */
|
||||
uint8_t Ethernet_Broadcast[MAX_MAC_LEN] =
|
||||
{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
|
||||
/* commonly used empty address for ethernet quick compare */
|
||||
uint8_t Ethernet_Empty_MAC[MAX_MAC_LEN] = { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
/* my local device data - MAC address */
|
||||
uint8_t Ethernet_MAC_Address[MAX_MAC_LEN] = { 0 };
|
||||
|
||||
static int eth802_sockfd = -1; /* 802.2 file handle */
|
||||
static struct sockaddr eth_addr = { 0 }; /* used for binding 802.2 */
|
||||
|
||||
bool ethernet_valid(void)
|
||||
{
|
||||
return (eth802_sockfd >= 0);
|
||||
}
|
||||
|
||||
void ethernet_cleanup(void)
|
||||
{
|
||||
if (ethernet_valid())
|
||||
close(eth802_sockfd);
|
||||
eth802_sockfd = -1;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------
|
||||
Portable function to set a socket into nonblocking mode.
|
||||
Calling this on a socket causes all future read() and write() calls on
|
||||
that socket to do only as much as they can immediately, and return
|
||||
without waiting.
|
||||
If no data can be read or written, they return -1 and set errno
|
||||
to EAGAIN (or EWOULDBLOCK).
|
||||
Thanks to Bjorn Reese for this code.
|
||||
----------------------------------------------------------------------*/
|
||||
int setNonblocking(int fd)
|
||||
{
|
||||
int flags;
|
||||
|
||||
if (-1 == (flags = fcntl(fd, F_GETFL, 0)))
|
||||
flags = 0;
|
||||
return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
|
||||
}
|
||||
|
||||
/* opens an 802.2 socket to receive and send packets */
|
||||
static int ethernet_bind(struct sockaddr *eth_addr, char *interface_name)
|
||||
{
|
||||
int sock_fd = -1; /* return value */
|
||||
int uid = 0;
|
||||
|
||||
fprintf(stderr, "ethernet: opening \"%s\"\n", interface_name);
|
||||
/* check to see if we are being run as root */
|
||||
uid = getuid();
|
||||
if (uid != 0) {
|
||||
fprintf(stderr,
|
||||
"ethernet: Unable to open an 802.2 socket. "
|
||||
"Try running with root priveleges.\n");
|
||||
return sock_fd;
|
||||
}
|
||||
/* note: on some systems you may have to add or enable in */
|
||||
/* modules.conf (or in modutils/alias on Debian with update-modules) */
|
||||
/* alias net-pf-17 af_packet */
|
||||
/* Then follow it by: # modprobe af_packet */
|
||||
|
||||
/* Attempt to open the socket for 802.2 ethernet frames */
|
||||
if ((sock_fd = socket(PF_INET, SOCK_PACKET, htons(ETH_P_802_2))) < 0) {
|
||||
/* Error occured */
|
||||
fprintf(stderr,
|
||||
"ethernet: Error opening socket: %s\n", strerror(errno));
|
||||
fprintf(stderr,
|
||||
"You might need to add the following to modules.conf\n"
|
||||
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
||||
"alias net-pf-17 af_packet\n"
|
||||
"Also, add af_packet to /etc/modules.\n"
|
||||
"Then follow it by:\n" "# modprobe af_packet\n");
|
||||
exit(-1);
|
||||
}
|
||||
/* Bind the socket to an address */
|
||||
eth_addr->sa_family = PF_INET;
|
||||
/* Clear the memory before copying */
|
||||
memset(eth_addr->sa_data, '\0', sizeof(eth_addr->sa_data));
|
||||
/* Strcpy the interface name into the address */
|
||||
strncpy(eth_addr->sa_data, interface_name,
|
||||
sizeof(eth_addr->sa_data) - 1);
|
||||
fprintf(stderr, "ethernet: binding \"%s\"\n", eth_addr->sa_data);
|
||||
/* Attempt to bind the socket to the interface */
|
||||
if (bind(sock_fd, eth_addr, sizeof(struct sockaddr)) != 0) {
|
||||
/* Bind problem, close socket and return */
|
||||
fprintf(stderr,
|
||||
"ethernet: Unable to bind 802.2 socket : %s\n",
|
||||
strerror(errno));
|
||||
fprintf(stderr,
|
||||
"You might need to add the following to modules.conf\n"
|
||||
"(or in /etc/modutils/alias on Debian with update-modules):\n"
|
||||
"alias net-pf-17 af_packet\n"
|
||||
"Also, add af_packet to /etc/modules.\n"
|
||||
"Then follow it by:\n" "# modprobe af_packet\n");
|
||||
/* Close the socket */
|
||||
close(sock_fd);
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
atexit(ethernet_cleanup);
|
||||
|
||||
return sock_fd;
|
||||
}
|
||||
|
||||
/* function to find the local ethernet MAC address */
|
||||
static int get_local_hwaddr(const char *ifname, unsigned char *mac)
|
||||
{
|
||||
struct ifreq ifr;
|
||||
int fd;
|
||||
int rv; /* return value - error value from df or ioctl call */
|
||||
|
||||
/* determine the local MAC address */
|
||||
strcpy(ifr.ifr_name, ifname);
|
||||
fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
|
||||
if (fd < 0)
|
||||
rv = fd;
|
||||
else {
|
||||
rv = ioctl(fd, SIOCGIFHWADDR, &ifr);
|
||||
if (rv >= 0) /* worked okay */
|
||||
memcpy(mac, ifr.ifr_hwaddr.sa_data, IFHWADDRLEN);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool ethernet_init(char *interface_name)
|
||||
{
|
||||
if (interface_name) {
|
||||
get_local_hwaddr(interface_name, Ethernet_MAC_Address);
|
||||
eth802_sockfd = ethernet_bind(ð_addr, interface_name);
|
||||
} else {
|
||||
get_local_hwaddr("eth0", Ethernet_MAC_Address);
|
||||
eth802_sockfd = ethernet_bind(ð_addr, "eth0");
|
||||
}
|
||||
|
||||
return ethernet_valid();
|
||||
}
|
||||
|
||||
/* function to send a packet out the 802.2 socket */
|
||||
/* returns number of bytes sent on success, negative on failure */
|
||||
int ethernet_send_pdu(BACNET_ADDRESS * dest, /* destination address */
|
||||
BACNET_NPDU_DATA * npdu_data, /* network information */
|
||||
uint8_t * pdu, /* any data to be sent - may be null */
|
||||
unsigned pdu_len)
|
||||
{ /* number of bytes of data */
|
||||
int i = 0; /* counter */
|
||||
int bytes = 0;
|
||||
BACNET_ADDRESS src = { 0 }; /* source address for npdu */
|
||||
uint8_t mtu[MAX_MPDU] = { 0 }; /* our buffer */
|
||||
int mtu_len = 0;
|
||||
|
||||
(void) npdu_data;
|
||||
/* load the BACnet address for NPDU data */
|
||||
for (i = 0; i < 6; i++) {
|
||||
src.mac[i] = Ethernet_MAC_Address[i];
|
||||
src.mac_len++;
|
||||
}
|
||||
|
||||
/* don't waste time if the socket is not valid */
|
||||
if (eth802_sockfd < 0) {
|
||||
fprintf(stderr, "ethernet: 802.2 socket is invalid!\n");
|
||||
return -1;
|
||||
}
|
||||
/* load destination ethernet MAC address */
|
||||
if (dest->mac_len == 6) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
mtu[i] = dest->mac[i];
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ethernet: invalid destination MAC address!\n");
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* load source ethernet MAC address */
|
||||
if (src.mac_len == 6) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
mtu[6 + i] = src.mac[i];
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr, "ethernet: invalid source MAC address!\n");
|
||||
return -3;
|
||||
}
|
||||
/* Logical PDU portion */
|
||||
mtu[14] = 0x82; /* DSAP for BACnet */
|
||||
mtu[15] = 0x82; /* SSAP for BACnet */
|
||||
mtu[16] = 0x03; /* Control byte in header */
|
||||
mtu_len = 17;
|
||||
if ((mtu_len + pdu_len) > MAX_MPDU) {
|
||||
fprintf(stderr, "ethernet: PDU is too big to send!\n");
|
||||
return -4;
|
||||
}
|
||||
memcpy(&mtu[mtu_len], pdu, pdu_len);
|
||||
mtu_len += pdu_len;
|
||||
/* packet length - only the logical portion, not the address */
|
||||
encode_unsigned16(&mtu[12], 3 + pdu_len);
|
||||
|
||||
/* Send the packet */
|
||||
bytes =
|
||||
sendto(eth802_sockfd, &mtu, mtu_len, 0,
|
||||
(struct sockaddr *) ð_addr, sizeof(struct sockaddr));
|
||||
/* did it get sent? */
|
||||
if (bytes < 0)
|
||||
fprintf(stderr, "ethernet: Error sending packet: %s\n",
|
||||
strerror(errno));
|
||||
|
||||
return bytes;
|
||||
}
|
||||
|
||||
/* receives an 802.2 framed packet */
|
||||
/* returns the number of octets in the PDU, or zero on failure */
|
||||
uint16_t ethernet_receive(BACNET_ADDRESS * src, /* source address */
|
||||
uint8_t * pdu, /* PDU data */
|
||||
uint16_t max_pdu, /* amount of space available in the PDU */
|
||||
unsigned timeout)
|
||||
{ /* number of milliseconds to wait for a packet */
|
||||
int received_bytes;
|
||||
uint8_t buf[MAX_MPDU] = { 0 }; /* data */
|
||||
uint16_t pdu_len = 0; /* return value */
|
||||
fd_set read_fds;
|
||||
int max;
|
||||
struct timeval select_timeout;
|
||||
|
||||
/* Make sure the socket is open */
|
||||
if (eth802_sockfd <= 0)
|
||||
return 0;
|
||||
|
||||
/* we could just use a non-blocking socket, but that consumes all
|
||||
the CPU time. We can use a timeout; it is only supported as
|
||||
a select. */
|
||||
if (timeout >= 1000) {
|
||||
select_timeout.tv_sec = timeout / 1000;
|
||||
select_timeout.tv_usec =
|
||||
1000 * (timeout - select_timeout.tv_sec * 1000);
|
||||
} else {
|
||||
select_timeout.tv_sec = 0;
|
||||
select_timeout.tv_usec = 1000 * timeout;
|
||||
}
|
||||
FD_ZERO(&read_fds);
|
||||
FD_SET(eth802_sockfd, &read_fds);
|
||||
max = eth802_sockfd;
|
||||
|
||||
if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0)
|
||||
received_bytes = read(eth802_sockfd, &buf[0], sizeof(buf));
|
||||
else
|
||||
return 0;
|
||||
|
||||
/* See if there is a problem */
|
||||
if (received_bytes < 0) {
|
||||
/* EAGAIN Non-blocking I/O has been selected */
|
||||
/* using O_NONBLOCK and no data */
|
||||
/* was immediately available for reading. */
|
||||
if (errno != EAGAIN)
|
||||
fprintf(stderr,
|
||||
"ethernet: Read error in receiving packet: %s\n",
|
||||
strerror(errno));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (received_bytes == 0)
|
||||
return 0;
|
||||
|
||||
/* the signature of an 802.2 BACnet packet */
|
||||
if ((buf[14] != 0x82) && (buf[15] != 0x82)) {
|
||||
/*fprintf(stderr,"ethernet: Non-BACnet packet\n"); */
|
||||
return 0;
|
||||
}
|
||||
/* copy the source address */
|
||||
src->mac_len = 6;
|
||||
memmove(src->mac, &buf[6], 6);
|
||||
|
||||
/* check destination address for when */
|
||||
/* the Ethernet card is in promiscious mode */
|
||||
if ((memcmp(&buf[0], Ethernet_MAC_Address, 6) != 0)
|
||||
&& (memcmp(&buf[0], Ethernet_Broadcast, 6) != 0)) {
|
||||
/*fprintf(stderr, "ethernet: This packet isn't for us\n"); */
|
||||
return 0;
|
||||
}
|
||||
|
||||
(void) decode_unsigned16(&buf[12], &pdu_len);
|
||||
pdu_len -= 3 /* DSAP, SSAP, LLC Control */ ;
|
||||
/* copy the buffer into the PDU */
|
||||
if (pdu_len < max_pdu)
|
||||
memmove(&pdu[0], &buf[17], pdu_len);
|
||||
/* ignore packets that are too large */
|
||||
else
|
||||
pdu_len = 0;
|
||||
|
||||
|
||||
return pdu_len;
|
||||
}
|
||||
|
||||
void ethernet_set_my_address(BACNET_ADDRESS * my_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
for (i = 0; i < 6; i++) {
|
||||
Ethernet_MAC_Address[i] = my_address->mac[i];
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ethernet_get_my_address(BACNET_ADDRESS * my_address)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
my_address->mac_len = 0;
|
||||
for (i = 0; i < 6; i++) {
|
||||
my_address->mac[i] = Ethernet_MAC_Address[i];
|
||||
my_address->mac_len++;
|
||||
}
|
||||
my_address->net = 0; /* DNET=0 is local only, no routing */
|
||||
my_address->len = 0;
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
my_address->adr[i] = 0;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ethernet_get_broadcast_address(BACNET_ADDRESS * dest)
|
||||
{ /* destination address */
|
||||
int i = 0; /* counter */
|
||||
|
||||
if (dest) {
|
||||
for (i = 0; i < 6; i++) {
|
||||
dest->mac[i] = Ethernet_Broadcast[i];
|
||||
}
|
||||
dest->mac_len = 6;
|
||||
dest->net = BACNET_BROADCAST_NETWORK;
|
||||
dest->len = 0; /* always zero when DNET is broadcast */
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
dest->adr[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
void ethernet_debug_address(const char *info, BACNET_ADDRESS * dest)
|
||||
{
|
||||
int i = 0; /* counter */
|
||||
|
||||
if (info)
|
||||
fprintf(stderr, "%s", info);
|
||||
if (dest) {
|
||||
fprintf(stderr, "Address:\n");
|
||||
fprintf(stderr, " MAC Length=%d\n", dest->mac_len);
|
||||
fprintf(stderr, " MAC Address=");
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
fprintf(stderr, "%02X ", (unsigned) dest->mac[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " Net=%hu\n", dest->net);
|
||||
fprintf(stderr, " Len=%d\n", dest->len);
|
||||
fprintf(stderr, " Adr=");
|
||||
for (i = 0; i < MAX_MAC_LEN; i++) {
|
||||
fprintf(stderr, "%02X ", (unsigned) dest->adr[i]);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,298 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* 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 <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <signal.h>
|
||||
#include <time.h>
|
||||
#include "config.h"
|
||||
#include "address.h"
|
||||
#include "bacdef.h"
|
||||
#include "handlers.h"
|
||||
#include "client.h"
|
||||
#include "bacdcode.h"
|
||||
#include "npdu.h"
|
||||
#include "apdu.h"
|
||||
#include "iam.h"
|
||||
#include "tsm.h"
|
||||
#include "device.h"
|
||||
#include "bacfile.h"
|
||||
#include "datalink.h"
|
||||
#include "net.h"
|
||||
#include "txbuf.h"
|
||||
|
||||
/* This is an example application using the BACnet Stack on Linux */
|
||||
bool Who_Is_Request = true;
|
||||
|
||||
/* buffers used for receiving */
|
||||
static uint8_t Rx_Buf[MAX_MPDU] = { 0 };
|
||||
|
||||
static void LocalIAmHandler(uint8_t * service_request,
|
||||
uint16_t service_len, BACNET_ADDRESS * src)
|
||||
{
|
||||
int len = 0;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
int segmentation = 0;
|
||||
uint16_t vendor_id = 0;
|
||||
|
||||
(void) src;
|
||||
(void) service_len;
|
||||
len = iam_decode_service_request(service_request,
|
||||
&device_id, &max_apdu, &segmentation, &vendor_id);
|
||||
fprintf(stderr, "Received I-Am Request");
|
||||
if (len != -1) {
|
||||
fprintf(stderr, " from %u!\n", device_id);
|
||||
address_add(device_id, max_apdu, src);
|
||||
} else
|
||||
fprintf(stderr, "!\n");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void Read_Properties(void)
|
||||
{
|
||||
uint32_t device_id = 0;
|
||||
bool status = false;
|
||||
unsigned max_apdu = 0;
|
||||
BACNET_ADDRESS src;
|
||||
bool next_device = false;
|
||||
static unsigned index = 0;
|
||||
static unsigned property = 0;
|
||||
/* list of required (and some optional and proprietary)
|
||||
properties in the Device Object. Note that this demo
|
||||
tests for error messages so that the device doesn't have
|
||||
to have all the properties listed here. */
|
||||
const int object_props[] = {
|
||||
PROP_OBJECT_IDENTIFIER,
|
||||
PROP_OBJECT_NAME,
|
||||
PROP_OBJECT_TYPE,
|
||||
PROP_SYSTEM_STATUS,
|
||||
PROP_VENDOR_NAME,
|
||||
PROP_VENDOR_IDENTIFIER,
|
||||
PROP_MODEL_NAME,
|
||||
PROP_FIRMWARE_REVISION,
|
||||
PROP_APPLICATION_SOFTWARE_VERSION,
|
||||
PROP_PROTOCOL_VERSION,
|
||||
PROP_PROTOCOL_CONFORMANCE_CLASS,
|
||||
PROP_PROTOCOL_SERVICES_SUPPORTED,
|
||||
PROP_PROTOCOL_OBJECT_TYPES_SUPPORTED,
|
||||
PROP_MAX_APDU_LENGTH_ACCEPTED,
|
||||
PROP_SEGMENTATION_SUPPORTED,
|
||||
PROP_LOCAL_TIME,
|
||||
PROP_LOCAL_DATE,
|
||||
PROP_UTC_OFFSET,
|
||||
PROP_DAYLIGHT_SAVINGS_STATUS,
|
||||
PROP_APDU_SEGMENT_TIMEOUT,
|
||||
PROP_APDU_TIMEOUT,
|
||||
PROP_NUMBER_OF_APDU_RETRIES,
|
||||
PROP_TIME_SYNCHRONIZATION_RECIPIENTS,
|
||||
PROP_MAX_MASTER,
|
||||
PROP_MAX_INFO_FRAMES,
|
||||
PROP_DEVICE_ADDRESS_BINDING,
|
||||
/* note: PROP_OBJECT_LIST is missing because
|
||||
the result can be very large. Read index 0
|
||||
which gives us the number of objects in the list,
|
||||
and then we can read index 1, 2.. n one by one,
|
||||
rather than trying to read the entire object
|
||||
list in one message. */
|
||||
/* some proprietary properties */
|
||||
514, 515,
|
||||
/* end of list */
|
||||
-1
|
||||
};
|
||||
|
||||
if (address_count()) {
|
||||
if (address_get_by_index(index, &device_id, &max_apdu, &src)) {
|
||||
if (object_props[property] < 0)
|
||||
next_device = true;
|
||||
else {
|
||||
/* note: if we wanted to do this synchronously, we would get the
|
||||
invoke ID from the sending of the request, and wait until we
|
||||
got the reply with matching invoke ID or the TSM of the
|
||||
invoke ID expired. This demo is doing things asynchronously. */
|
||||
status = Send_Read_Property_Request(device_id, /* destination device */
|
||||
OBJECT_DEVICE,
|
||||
device_id, object_props[property], BACNET_ARRAY_ALL);
|
||||
if (status)
|
||||
property++;
|
||||
}
|
||||
} else
|
||||
next_device = true;
|
||||
if (next_device) {
|
||||
next_device = false;
|
||||
index++;
|
||||
if (index >= MAX_ADDRESS_CACHE)
|
||||
index = 0;
|
||||
property = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static void Init_Service_Handlers(void)
|
||||
{
|
||||
/* we need to handle who-is to support dynamic device binding */
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_WHO_IS,
|
||||
handler_who_is);
|
||||
apdu_set_unconfirmed_handler(SERVICE_UNCONFIRMED_I_AM,
|
||||
LocalIAmHandler);
|
||||
|
||||
/* set the handler for all the services we don't implement */
|
||||
/* It is required to send the proper reject message... */
|
||||
apdu_set_unrecognized_service_handler_handler
|
||||
(handler_unrecognized_service);
|
||||
/* Set the handlers for any confirmed services that we support. */
|
||||
/* We must implement read property - it's required! */
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_WRITE_PROPERTY,
|
||||
handler_write_property);
|
||||
apdu_set_confirmed_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||
handler_atomic_read_file);
|
||||
/* handle the data coming back from confirmed requests */
|
||||
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_READ_PROPERTY,
|
||||
handler_read_property_ack);
|
||||
apdu_set_confirmed_ack_handler(SERVICE_CONFIRMED_ATOMIC_READ_FILE,
|
||||
handler_atomic_read_file_ack);
|
||||
}
|
||||
|
||||
static void print_address_cache(void)
|
||||
{
|
||||
unsigned i, j;
|
||||
BACNET_ADDRESS address;
|
||||
uint32_t device_id = 0;
|
||||
unsigned max_apdu = 0;
|
||||
|
||||
fprintf(stderr, "Device\tMAC\tMaxAPDU\tNet\n");
|
||||
for (i = 0; i < MAX_ADDRESS_CACHE; i++) {
|
||||
if (address_get_by_index(i, &device_id, &max_apdu, &address)) {
|
||||
fprintf(stderr, "%u\t", device_id);
|
||||
for (j = 0; j < address.mac_len; j++) {
|
||||
fprintf(stderr, "%02X", address.mac[j]);
|
||||
}
|
||||
fprintf(stderr, "\t");
|
||||
fprintf(stderr, "%hu\t", max_apdu);
|
||||
fprintf(stderr, "%hu\n", address.net);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void print_tsm_stats(void)
|
||||
{
|
||||
int idle = 0;
|
||||
int total = 0;
|
||||
|
||||
idle = tsm_transaction_idle_count();
|
||||
total = MAX_TSM_TRANSACTIONS;
|
||||
fprintf(stderr, "TSM: %d idle of %d transactions\n", idle, total);
|
||||
}
|
||||
|
||||
static void sig_handler(int signo)
|
||||
{
|
||||
datalink_cleanup();
|
||||
print_address_cache();
|
||||
print_tsm_stats();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
BACNET_ADDRESS src = { 0 }; /* address where message came from */
|
||||
uint16_t pdu_len = 0;
|
||||
unsigned timeout = 100; /* milliseconds */
|
||||
unsigned count = 0; /* milliseconds */
|
||||
time_t start_time;
|
||||
time_t new_time = 0;
|
||||
|
||||
start_time = time(NULL); /* get current time */
|
||||
/* Linux specials */
|
||||
signal(SIGINT, sig_handler);
|
||||
signal(SIGHUP, sig_handler);
|
||||
signal(SIGTERM, sig_handler);
|
||||
/* setup this BACnet Server device */
|
||||
Device_Set_Object_Instance_Number(111);
|
||||
Init_Service_Handlers();
|
||||
#ifdef BACDL_ETHERNET
|
||||
/* init the physical layer */
|
||||
if (!ethernet_init("eth0"))
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef BACDL_BIP
|
||||
bip_set_interface("eth0");
|
||||
bip_set_port(0xBAC0);
|
||||
if (!bip_init())
|
||||
return 1;
|
||||
#endif
|
||||
#ifdef BACDL_ARCNET
|
||||
if (!arcnet_init("arc0"))
|
||||
return 1;
|
||||
#endif
|
||||
|
||||
/* loop forever */
|
||||
for (;;) {
|
||||
/* input */
|
||||
new_time = time(NULL);
|
||||
/* returns 0 bytes on timeout */
|
||||
pdu_len = datalink_receive(&src, &Rx_Buf[0], MAX_MPDU, timeout);
|
||||
|
||||
/* process */
|
||||
if (pdu_len) {
|
||||
npdu_handler(&src, &Rx_Buf[0], pdu_len);
|
||||
}
|
||||
if (new_time > start_time) {
|
||||
tsm_timer_milliseconds(new_time - start_time * 1000);
|
||||
start_time = new_time;
|
||||
}
|
||||
if (I_Am_Request) {
|
||||
I_Am_Request = false;
|
||||
iam_send(&Handler_Transmit_Buffer[0]);
|
||||
} else if (Who_Is_Request) {
|
||||
Who_Is_Request = false;
|
||||
Send_WhoIs(-1, -1);
|
||||
}
|
||||
/* output */
|
||||
/* some round robin task switching */
|
||||
count++;
|
||||
switch (count) {
|
||||
case 1:
|
||||
/* used for testing, but kind of noisy on the network */
|
||||
/*Read_Properties(); */
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
count = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* blink LEDs, Turn on or off outputs, etc */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,99 +0,0 @@
|
||||
/**************************************************************************
|
||||
*
|
||||
* Copyright (C) 2005 Steve Karg <skarg@users.sourceforge.net>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files (the
|
||||
* "Software"), to deal in the Software without restriction, including
|
||||
* without limitation the rights to use, copy, modify, merge, publish,
|
||||
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||
* permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included
|
||||
* in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef NET_H
|
||||
#define NET_H
|
||||
|
||||
/* common unix sockets headers needed */
|
||||
#include <sys/types.h> /* basic system data types */
|
||||
#include <sys/time.h> /* timeval{} for select() */
|
||||
#include <time.h> /* timespec{} for pselect() */
|
||||
#include <netinet/in.h> /* sockaddr_in{} and other Internet defns */
|
||||
#include <arpa/inet.h> /* inet(3) functions */
|
||||
#include <fcntl.h> /* for nonblocking */
|
||||
#include <netdb.h>
|
||||
#include <errno.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h> /* for S_xxx file mode constants */
|
||||
#include <sys/uio.h> /* for iovec{} and readv/writev */
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/un.h> /* for Unix domain sockets */
|
||||
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
# include <sys/select.h> /* for convenience */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
# include <poll.h> /* for convenience */
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_STRINGS_H
|
||||
# include <strings.h> /* for convenience */
|
||||
#endif
|
||||
|
||||
/* Three headers are normally needed for socket/file ioctl's:
|
||||
* <sys/ioctl.h>, <sys/filio.h>, and <sys/sockio.h>.
|
||||
*/
|
||||
#ifdef HAVE_SYS_IOCTL_H
|
||||
# include <sys/ioctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_FILIO_H
|
||||
# include <sys/filio.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_SOCKIO_H
|
||||
# include <sys/sockio.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_PTHREAD_H
|
||||
# include <pthread.h>
|
||||
#endif
|
||||
|
||||
|
||||
#define ENUMS
|
||||
#include <sys/socket.h>
|
||||
#include <net/route.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <features.h> /* for the glibc version number */
|
||||
#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
|
||||
#include <netpacket/packet.h>
|
||||
#include <net/ethernet.h> /* the L2 protocols */
|
||||
#else
|
||||
#include <asm/types.h>
|
||||
#include <linux/if_packet.h>
|
||||
#include <linux/if_arcnet.h>
|
||||
#include <linux/if_ether.h>
|
||||
#endif
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
/*####COPYRIGHTBEGIN####
|
||||
-------------------------------------------
|
||||
Copyright (C) 2004 Steve Karg
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation; either version 2
|
||||
of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to:
|
||||
The Free Software Foundation, Inc.
|
||||
59 Temple Place - Suite 330
|
||||
Boston, MA 02111-1307
|
||||
USA.
|
||||
|
||||
As a special exception, if other files instantiate templates or
|
||||
use macros or inline functions from this file, or you compile
|
||||
this file and link it with other works to produce a work based
|
||||
on this file, this file does not by itself cause the resulting
|
||||
work to be covered by the GNU General Public License. However
|
||||
the source code for this file must still be made available in
|
||||
accordance with section (3) of the GNU General Public License.
|
||||
|
||||
This exception does not invalidate any other reasons why a work
|
||||
based on this file might be covered by the GNU General Public
|
||||
License.
|
||||
-------------------------------------------
|
||||
####COPYRIGHTEND####*/
|
||||
|
||||
/* The module handles sending data out the RS-485 port */
|
||||
/* and handles receiving data from the RS-485 port. */
|
||||
/* Customize this file for your specific hardware */
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "mstp.h"
|
||||
|
||||
/* Transmits a Frame on the wire */
|
||||
void RS485_Send_Frame(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) */
|
||||
|
||||
/* in order to avoid line contention */
|
||||
while (mstp_port->Turn_Around_Waiting) {
|
||||
/* wait, yield, or whatever */
|
||||
}
|
||||
|
||||
/* Disable the receiver, and enable the transmit line driver. */
|
||||
|
||||
while (nbytes) {
|
||||
putc(*buffer, stderr);
|
||||
buffer++;
|
||||
nbytes--;
|
||||
}
|
||||
|
||||
/* Wait until the final stop bit of the most significant CRC octet */
|
||||
/* has been transmitted but not more than Tpostdrive. */
|
||||
|
||||
/* Disable the transmit line driver. */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* called by timer, interrupt(?) or other thread */
|
||||
void RS485_Check_UART_Data(struct mstp_port_struct_t *mstp_port)
|
||||
{
|
||||
if (mstp_port->ReceiveError == true) {
|
||||
/* wait for state machine to clear this */
|
||||
}
|
||||
/* wait for state machine to read from the DataRegister */
|
||||
else if (mstp_port->DataAvailable == false) {
|
||||
/* check for data */
|
||||
|
||||
/* if error, */
|
||||
/* ReceiveError = TRUE; */
|
||||
/* return; */
|
||||
|
||||
mstp_port->DataRegister = 0; /* FIXME: Get this data from UART or buffer */
|
||||
|
||||
/* if data is ready, */
|
||||
/* DataAvailable = TRUE; */
|
||||
/* return; */
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user