diff --git a/bacnet-stack/Makefile b/bacnet-stack/Makefile index 2b2b43d4..649b45a3 100644 --- a/bacnet-stack/Makefile +++ b/bacnet-stack/Makefile @@ -6,12 +6,14 @@ BASEDIR = . #CFLAGS = -Wall -I. -O2 -g # Note: you can strip out symbols using the strip command # to get an idea of how big the compile really is. -#CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_MSTP=1 #CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_ETHERNET=1 -CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_BIP=1 +CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_ARCNET=1 +#CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_MSTP=1 +#CFLAGS = -Wall -I. -Iports/linux -g -DBACDL_BIP=1 SRCS = ports/linux/main.c \ ports/linux/ethernet.c \ + ports/linux/arcnet.c \ ports/linux/bip-init.c \ bip.c \ dlmstp.c \ diff --git a/bacnet-stack/arcnet.h b/bacnet-stack/arcnet.h index 0d2bf3f5..93726c26 100644 --- a/bacnet-stack/arcnet.h +++ b/bacnet-stack/arcnet.h @@ -34,15 +34,21 @@ #ifndef ARCNET_H #define ARCNET_H +#include #include +#include #include "bacdef.h" // specific defines for ARCNET #define MAX_HEADER (1+1+2+2+1+1+1+1) #define MAX_MPDU (MAX_HEADER+MAX_PDU) +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + bool arcnet_valid(void); -bool arcnet_cleanup(void); +void arcnet_cleanup(void); bool arcnet_init(char *interface_name); /* function to send a packet out the 802.2 socket */ @@ -60,13 +66,20 @@ int arcnet_send_pdu( uint8_t *pdu, // any data to be sent - may be null unsigned pdu_len); // number of bytes of data -// receives an 802.2 framed packet +// 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 + uint16_t max_pdu, // amount of space available in the PDU + unsigned timeout); // milliseconds to wait for a packet void arcnet_get_my_address(BACNET_ADDRESS *my_address); +void arcnet_get_broadcast_address( + BACNET_ADDRESS *dest); // destination address + +#ifdef __cplusplus +} +#endif /* __cplusplus */ #endif diff --git a/bacnet-stack/datalink.c b/bacnet-stack/datalink.c index 4b46c397..910849be 100644 --- a/bacnet-stack/datalink.c +++ b/bacnet-stack/datalink.c @@ -46,6 +46,12 @@ int datalink_send_pdu( uint8_t *pdu, // any data to be sent - may be null unsigned pdu_len) // number of bytes of data { +#ifdef BACDL_ARCNET + return arcnet_send_pdu( + dest, + pdu, + pdu_len); +#endif #ifdef BACDL_MSTP return dlmstp_send_pdu( dest, @@ -73,6 +79,13 @@ uint16_t datalink_receive( uint16_t max_pdu, // amount of space available in the PDU unsigned timeout) // number of milliseconds to wait for a packet { + #ifdef BACDL_ARCNET + return arcnet_receive( + src, + pdu, + max_pdu, + timeout); + #endif #ifdef BACDL_MSTP return dlmstp_receive( src, @@ -99,6 +112,9 @@ uint16_t datalink_receive( void datalink_get_broadcast_address( BACNET_ADDRESS *dest) // destination address { +#ifdef BACDL_ARCNET + arcnet_get_broadcast_address(dest); +#endif #ifdef BACDL_MSTP dlmstp_get_broadcast_address(dest); #endif @@ -113,6 +129,9 @@ void datalink_get_broadcast_address( void datalink_get_my_address( BACNET_ADDRESS *my_address) { +#ifdef BACDL_ARCNET + arcnet_get_my_address(my_address); +#endif #ifdef BACDL_MSTP dlmstp_get_my_address(my_address); #endif diff --git a/bacnet-stack/datalink.h b/bacnet-stack/datalink.h index eae7ec61..6d457c08 100644 --- a/bacnet-stack/datalink.h +++ b/bacnet-stack/datalink.h @@ -43,6 +43,10 @@ #include "ethernet.h" #endif +#ifdef BACDL_ARCNET +#include "arcnet.h" +#endif + #ifdef BACDL_MSTP #include "dlmstp.h" #endif diff --git a/bacnet-stack/ports/linux/arcnet.c b/bacnet-stack/ports/linux/arcnet.c new file mode 100644 index 00000000..0da69b52 --- /dev/null +++ b/bacnet-stack/ports/linux/arcnet.c @@ -0,0 +1,321 @@ +#include +#include "bacdef.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 + +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 + + 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]; + fprintf(stderr,"arcnet: MAC=%02Xh\n",ARCNET_MAC_Address); + + atexit(arcnet_cleanup); + + return sock_fd; +} + +bool arcnet_init(char *interface_name) +{ + ARCNET_Sock_FD = arcnet_bind(interface_name); + + return arcnet_valid(); +} + +/* function to send a packet out the socket */ +/* returns number of bytes sent on success, negative on failure */ +int arcnet_send( + BACNET_ADDRESS *dest, // destination address + BACNET_ADDRESS *src, // source address + uint8_t *pdu, // any data to be sent - may be null + unsigned pdu_len) // number of bytes of data +{ + int bytes = 0; + uint8_t mtu[512] = { 0 }; + int mtu_len = 0; + struct archdr *pkt = (struct archdr *)mtu; + + // 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; + } + if ((ARC_HDR_SIZE + pdu_len) > 512) + { + fprintf(stderr, "arcnet: PDU is too big to send!\n"); + return -4; + } + /* 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; /* Control byte in header */ + memcpy(&pkt->soft.raw[4], pdu, pdu_len); + /* packet length */ + mtu_len = ARC_HDR_SIZE + 4 /*SC,DSAP,SSAP,LLC*/ + 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; +} + +/* 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 + 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 + + src.mac[0] = ARCNET_MAC_Address; + src.mac_len = 1; + return arcnet_send(dest, // destination address + &src, // source address + pdu, // any data to be sent - may be null + pdu_len); // number of bytes of data +} + +// receives an 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 from %02Xh (proto==%02Xh)\n", + received_bytes, 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; // 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; // len=0 denotes broadcast address + for (i = 0; i < MAX_MAC_LEN; i++) + { + dest->adr[i] = 0; + } + } + + return; +} + diff --git a/bacnet-stack/ports/linux/ethernet.c b/bacnet-stack/ports/linux/ethernet.c index 71a277f0..1ad88b47 100644 --- a/bacnet-stack/ports/linux/ethernet.c +++ b/bacnet-stack/ports/linux/ethernet.c @@ -316,7 +316,7 @@ uint16_t ethernet_receive( max = eth802_sockfd; if (select(max + 1, &read_fds, NULL, NULL, &select_timeout) > 0) - received_bytes = read(eth802_sockfd, &buf[0], MAX_MPDU); + received_bytes = read(eth802_sockfd, &buf[0], sizeof(buf)); else return 0; diff --git a/bacnet-stack/ports/linux/net.h b/bacnet-stack/ports/linux/net.h index a75c375d..51e892da 100644 --- a/bacnet-stack/ports/linux/net.h +++ b/bacnet-stack/ports/linux/net.h @@ -79,6 +79,7 @@ #include #include #include +#include #include /* for the glibc version number */ #if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1 #include @@ -86,7 +87,8 @@ #else #include #include -#include /* The L2 protocols */ +#include +#include #endif #include #include